From c01dd40a6566b62e3ae85f6c0024c147cf2d78ff Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Mon, 19 Nov 2012 19:03:59 +0100 Subject: [PATCH 01/58] HHH-5910 - Fix and test --- .../RevisionInfoConfiguration.java | 2 +- .../metadata/AuditMetadataGenerator.java | 4 +- .../metadata/CollectionMetadataGenerator.java | 2 +- .../configuration/metadata/MetadataTools.java | 15 ++++-- .../abstractparent/AbstractEntity.java | 31 ++++++++++++ .../AuditedAbstractParentTest.java | 50 +++++++++++++++++++ .../abstractparent/EffectiveEntity1.java | 26 ++++++++++ 7 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AbstractEntity.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AuditedAbstractParentTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/EffectiveEntity1.java diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java index 43a98ed50d..14f6c29020 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/RevisionInfoConfiguration.java @@ -93,7 +93,7 @@ public class RevisionInfoConfiguration { private Document generateDefaultRevisionInfoXmlMapping() { Document document = XMLHelper.getDocumentFactory().createDocument(); - Element class_mapping = MetadataTools.createEntity(document, new AuditTableData(null, null, globalCfg.getDefaultSchemaName(), globalCfg.getDefaultCatalogName()), null); + Element class_mapping = MetadataTools.createEntity(document, new AuditTableData(null, null, globalCfg.getDefaultSchemaName(), globalCfg.getDefaultCatalogName()), null, null); class_mapping.addAttribute("name", revisionInfoEntityName); class_mapping.addAttribute("table", "REVINFO"); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java index c2660da98c..c3cb4f3c6b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java @@ -362,7 +362,7 @@ public final class AuditMetadataGenerator { PersistentClass pc, EntityXmlMappingData xmlMappingData, AuditTableData auditTableData, IdMappingData idMapper) { Element class_mapping = MetadataTools.createEntity(xmlMappingData.getMainXmlMapping(), auditTableData, - pc.getDiscriminatorValue()); + pc.getDiscriminatorValue(), pc.isAbstract()); ExtendedPropertyMapper propertyMapper = new MultiPropertyMapper(); // Checking if there is a discriminator column @@ -387,7 +387,7 @@ public final class AuditMetadataGenerator { String inheritanceMappingType) { String extendsEntityName = verEntCfg.getAuditEntityName(pc.getSuperclass().getEntityName()); Element class_mapping = MetadataTools.createSubclassEntity(xmlMappingData.getMainXmlMapping(), - inheritanceMappingType, auditTableData, extendsEntityName, pc.getDiscriminatorValue()); + inheritanceMappingType, auditTableData, extendsEntityName, pc.getDiscriminatorValue(), pc.isAbstract()); // The id and revision type is already mapped in the parent diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java index f90fa5c96a..907212bb82 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java @@ -531,7 +531,7 @@ public final class CollectionMetadataGenerator { String catalog = mainGenerator.getCatalog(propertyAuditingData.getJoinTable().catalog(), propertyValue.getCollectionTable()); Element middleEntityXml = MetadataTools.createEntity(xmlMappingData.newAdditionalMapping(), - new AuditTableData(auditMiddleEntityName, auditMiddleTableName, schema, catalog), null); + new AuditTableData(auditMiddleEntityName, auditMiddleTableName, schema, catalog), null, null); Element middleEntityXmlId = middleEntityXml.addElement("composite-id"); // If there is a where clause on the relation, adding it to the middle entity. diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java index 6dcab46545..c0e3315335 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/MetadataTools.java @@ -157,7 +157,7 @@ public class MetadataTools { } private static Element createEntityCommon(Document document, String type, AuditTableData auditTableData, - String discriminatorValue) { + String discriminatorValue, Boolean isAbstract) { Element hibernate_mapping = document.addElement("hibernate-mapping"); hibernate_mapping.addAttribute("auto-import", "false"); @@ -183,16 +183,21 @@ public class MetadataTools { class_mapping.addAttribute("catalog", auditTableData.getCatalog()); } + if (isAbstract != null) { + class_mapping.addAttribute("abstract", isAbstract.toString()); + } + return class_mapping; } - public static Element createEntity(Document document, AuditTableData auditTableData, String discriminatorValue) { - return createEntityCommon(document, "class", auditTableData, discriminatorValue); + public static Element createEntity(Document document, AuditTableData auditTableData, String discriminatorValue, + Boolean isAbstract) { + return createEntityCommon(document, "class", auditTableData, discriminatorValue, isAbstract); } public static Element createSubclassEntity(Document document, String subclassType, AuditTableData auditTableData, - String extendsEntityName, String discriminatorValue) { - Element class_mapping = createEntityCommon(document, subclassType, auditTableData, discriminatorValue); + String extendsEntityName, String discriminatorValue, Boolean isAbstract) { + Element class_mapping = createEntityCommon(document, subclassType, auditTableData, discriminatorValue, isAbstract); class_mapping.addAttribute("extends", extendsEntityName); diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AbstractEntity.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AbstractEntity.java new file mode 100644 index 0000000000..d4700aac0d --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AbstractEntity.java @@ -0,0 +1,31 @@ +package org.hibernate.envers.test.integration.inheritance.tableperclass.abstractparent; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; + +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +@Audited +public abstract class AbstractEntity { + @Id + public Long id; + + @Column + public String commonField; + + public AbstractEntity() { + } + + protected AbstractEntity(Long id, String commonField) { + this.commonField = commonField; + this.id = id; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AuditedAbstractParentTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AuditedAbstractParentTest.java new file mode 100644 index 0000000000..73553b5472 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/AuditedAbstractParentTest.java @@ -0,0 +1,50 @@ +package org.hibernate.envers.test.integration.inheritance.tableperclass.abstractparent; + +import java.util.Iterator; +import javax.persistence.EntityManager; + +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.mapping.Table; +import org.hibernate.testing.TestForIssue; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue(jiraKey = "HHH-5910") +public class AuditedAbstractParentTest extends BaseEnversJPAFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { AbstractEntity.class, EffectiveEntity1.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 + em.getTransaction().begin(); + EffectiveEntity1 entity = new EffectiveEntity1( 1L, "commonField", "specificField1" ); + em.persist( entity ); + em.getTransaction().commit(); + + em.close(); + } + + @Test + public void testAbstractTableExistence() { + Iterator tableIterator = getCfg().getTableMappings(); + while ( tableIterator.hasNext() ) { + Table table = tableIterator.next(); + if ( "AbstractEntity_AUD".equals( table.getName() ) ) { + Assert.assertFalse( table.isPhysicalTable() ); + return; + } + } + Assert.fail(); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/EffectiveEntity1.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/EffectiveEntity1.java new file mode 100644 index 0000000000..75a8430d93 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/inheritance/tableperclass/abstractparent/EffectiveEntity1.java @@ -0,0 +1,26 @@ +package org.hibernate.envers.test.integration.inheritance.tableperclass.abstractparent; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Table(name = "ENTITY_1") +@Audited +public class EffectiveEntity1 extends AbstractEntity { + @Column + public String specificField1; + + public EffectiveEntity1() { + } + + public EffectiveEntity1(Long id, String commonField, String specificField1) { + super( id, commonField ); + this.specificField1 = specificField1; + } +} From 4b2871cfba53541d34a74a07b1fc588ce3eaaa51 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 20 Nov 2012 13:19:57 -0600 Subject: [PATCH 02/58] HHH-1168 - Problem combining locking and paging on Oracle --- .../java/org/hibernate/dialect/Dialect.java | 4 + .../hibernate/dialect/Oracle8iDialect.java | 4 + .../internal/classic/QueryTranslatorImpl.java | 10 +- .../hibernate/internal/CoreMessageLogger.java | 8 ++ .../org/hibernate/internal/SQLQueryImpl.java | 5 +- .../internal/StoredProcedureOutputsImpl.java | 6 +- .../java/org/hibernate/loader/Loader.java | 125 ++++++++++++++--- ...cBatchingCollectionInitializerBuilder.java | 7 +- .../loader/criteria/CriteriaLoader.java | 54 +++++++- .../hibernate/loader/custom/CustomLoader.java | 36 +++++ .../DynamicBatchingEntityLoaderBuilder.java | 6 +- .../org/hibernate/loader/hql/QueryLoader.java | 29 +++- .../hibernate/test/locking/paging/Door.java | 61 ++++++++ .../locking/paging/PagingAndLockingTest.java | 131 ++++++++++++++++++ .../src/test/resources/hibernate.properties | 2 +- .../org/hibernate/jpa/internal/QueryImpl.java | 16 +-- .../jpa/test/lock/QueryLockingTest.java | 26 ++++ 17 files changed, 484 insertions(+), 46 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 90a0690496..303307caaf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2399,4 +2399,8 @@ public abstract class Dialect implements ConversionContext { public boolean forceLobAsLastValue() { return false; } + + public boolean supportsLockingAndPaging() { + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java index 5dcbc459dc..d18d13cfcf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java @@ -577,4 +577,8 @@ public class Oracle8iDialect extends Dialect { return true; } + @Override + public boolean supportsLockingAndPaging() { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java index 0202094c3b..6d5362fa5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/classic/QueryTranslatorImpl.java @@ -953,7 +953,8 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator if ( stats ) startTime = System.currentTimeMillis(); try { - final ResultSet rs = executeQueryStatement( queryParameters, false, session ); + final List afterLoadActions = new ArrayList(); + final ResultSet rs = executeQueryStatement( queryParameters, false, afterLoadActions, session ); final PreparedStatement st = (PreparedStatement) rs.getStatement(); HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer()); Iterator result = new IteratorImpl( rs, st, session, queryParameters.isReadOnly( session ), returnTypes, getColumnNames(), hi ); @@ -1094,8 +1095,13 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator } @Override - protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException { + protected String applyLocks( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) throws QueryException { // can't cache this stuff either (per-invocation) + final LockOptions lockOptions = parameters.getLockOptions(); final String result; if ( lockOptions == null || ( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 9584f9725a..88f1dab89d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1583,4 +1583,12 @@ public interface CoreMessageLogger extends BasicLogger { id = 443 ) void tooManyInExpressions(String dialectName, int limit, String paramName, int size); + + @LogMessage(level = WARN) + @Message( + value = "Encountered request which combined locking and paging, however dialect reports that database does " + + "not support that combination. Results will be locked after initial query executes", + id = 444 + ) + void delayedLockingDueToPaging(); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java index 657ab3376b..fa1af91803 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SQLQueryImpl.java @@ -68,6 +68,7 @@ public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery { private Collection querySpaces; private final boolean callable; + private final LockOptions lockOptions = new LockOptions(); /** * Constructs a SQLQueryImpl given a sql query defined in the mappings. @@ -245,8 +246,8 @@ public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery { @Override public LockOptions getLockOptions() { - //we never need to apply locks to the SQL - return null; + //we never need to apply locks to the SQL, however the native-sql loader handles this specially + return lockOptions; } public SQLQuery addScalar(final String columnAlias, final Type type) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java index 31e5e1f77f..7ece485945 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StoredProcedureOutputsImpl.java @@ -26,6 +26,7 @@ package org.hibernate.internal; import java.sql.CallableStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -299,6 +300,8 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs { this.session = session; } + // todo : this would be a great way to add locking to stored procedure support (at least where returning entities). + public List processResultSet(ResultSet resultSet) throws SQLException { super.autoDiscoverTypes( resultSet ); return super.processResultSet( @@ -307,7 +310,8 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs { session, true, null, - Integer.MAX_VALUE + Integer.MAX_VALUE, + Collections.emptyList() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 2370a7c921..3d061567fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -31,6 +31,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -47,6 +48,7 @@ import org.hibernate.LockOptions; import org.hibernate.QueryException; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; +import org.hibernate.Session; import org.hibernate.StaleObjectStateException; import org.hibernate.WrongClassException; import org.hibernate.cache.spi.FilterKey; @@ -59,6 +61,7 @@ import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.dialect.pagination.NoopLimitHandler; import org.hibernate.engine.internal.TwoPhaseLoad; import org.hibernate.engine.jdbc.ColumnNameCache; +import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityUniqueKey; import org.hibernate.engine.spi.PersistenceContext; @@ -195,7 +198,11 @@ public abstract class Loader { * empty superclass implementation merely returns its first * argument. */ - protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws HibernateException { + protected String applyLocks( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) throws HibernateException { return sql; } @@ -227,13 +234,48 @@ public abstract class Loader { /** * Modify the SQL, adding lock hints and comments, if necessary */ - protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect) - throws HibernateException { + protected String preprocessSQL( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) throws HibernateException { + sql = applyLocks( sql, parameters, dialect, afterLoadActions ); + return getFactory().getSettings().isCommentsEnabled() + ? prependComment( sql, parameters ) + : sql; + } - sql = applyLocks( sql, parameters.getLockOptions(), dialect ); + protected static interface AfterLoadAction { + public void afterLoad(SessionImplementor session, Object entity, Loadable persister); + } - return getFactory().getSettings().isCommentsEnabled() ? - prependComment( sql, parameters ) : sql; + protected boolean shouldDelayLockingDueToPaging( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) { + final LockOptions lockOptions = parameters.getLockOptions(); + final RowSelection rowSelection = parameters.getRowSelection(); + final LimitHandler limitHandler = dialect.buildLimitHandler( sql, rowSelection ); + if ( LimitHelper.useLimit( limitHandler, rowSelection ) ) { + // user has requested a combination of paging and locking. See if the dialect supports that + // (ahem, Oracle...) + if ( ! dialect.supportsLockingAndPaging() ) { + LOG.delayedLockingDueToPaging(); + afterLoadActions.add( + new AfterLoadAction() { + private final LockOptions originalLockOptions = lockOptions.makeCopy(); + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); + } + } + ); + parameters.setLockOptions( new LockOptions() ); + } + return true; + } + return false; } private String prependComment(String sql, QueryParameters parameters) { @@ -351,7 +393,7 @@ public abstract class Loader { resultSet, session, queryParameters.isReadOnly( session ) - ); + ); session.getPersistenceContext().initializeNonLazyCollections(); return result; } @@ -410,7 +452,7 @@ public abstract class Loader { resultSet, session, queryParameters.isReadOnly( session ) - ); + ); session.getPersistenceContext().initializeNonLazyCollections(); return result; } @@ -843,7 +885,9 @@ public abstract class Loader { selection.getMaxRows() : Integer.MAX_VALUE; - final ResultSet rs = executeQueryStatement( queryParameters, false, session ); + final List afterLoadActions = new ArrayList(); + + final ResultSet rs = executeQueryStatement( queryParameters, false, afterLoadActions, session ); final Statement st = rs.getStatement(); // would be great to move all this below here into another method that could also be used @@ -853,7 +897,7 @@ public abstract class Loader { // that I could do the control breaking at the means to know when to stop try { - return processResultSet( rs, queryParameters, session, returnProxies, forcedResultTransformer, maxRows ); + return processResultSet( rs, queryParameters, session, returnProxies, forcedResultTransformer, maxRows, afterLoadActions ); } finally { st.close(); @@ -867,7 +911,8 @@ public abstract class Loader { SessionImplementor session, boolean returnProxies, ResultTransformer forcedResultTransformer, - int maxRows) throws SQLException { + int maxRows, + List afterLoadActions) throws SQLException { final int entitySpan = getEntityPersisters().length; final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session ); final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() ); @@ -902,8 +947,16 @@ public abstract class Loader { LOG.tracev( "Done processing result set ({0} rows)", count ); - initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) ); - if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session ); + initializeEntitiesAndCollections( + hydratedObjects, + rs, + session, + queryParameters.isReadOnly( session ), + afterLoadActions + ); + if ( createSubselects ) { + createSubselects( subselectResultKeys, queryParameters, session ); + } return results; } @@ -989,8 +1042,22 @@ public abstract class Loader { final List hydratedObjects, final Object resultSetId, final SessionImplementor session, - final boolean readOnly) - throws HibernateException { + final boolean readOnly) throws HibernateException { + initializeEntitiesAndCollections( + hydratedObjects, + resultSetId, + session, + readOnly, + Collections.emptyList() + ); + } + + private void initializeEntitiesAndCollections( + final List hydratedObjects, + final Object resultSetId, + final SessionImplementor session, + final boolean readOnly, + List afterLoadActions) throws HibernateException { final CollectionPersister[] collectionPersisters = getCollectionPersisters(); if ( collectionPersisters != null ) { @@ -1042,9 +1109,19 @@ public abstract class Loader { // split off from initializeEntity. It *must* occur after // endCollectionLoad to ensure the collection is in the // persistence context. - if ( hydratedObjects!=null ) { + if ( hydratedObjects != null ) { for ( Object hydratedObject : hydratedObjects ) { TwoPhaseLoad.postLoad( hydratedObject, session, post ); + if ( afterLoadActions != null ) { + for ( AfterLoadAction afterLoadAction : afterLoadActions ) { + final EntityEntry entityEntry = session.getPersistenceContext().getEntry( hydratedObject ); + if ( entityEntry == null ) { + // big problem + throw new HibernateException( "Could not locate EntityEntry immediately after two-phase load" ); + } + afterLoadAction.afterLoad( session, hydratedObject, (Loadable) entityEntry.getPersister() ); + } + } } } } @@ -1721,15 +1798,17 @@ public abstract class Loader { protected ResultSet executeQueryStatement( final QueryParameters queryParameters, final boolean scroll, + List afterLoadActions, final SessionImplementor session) throws SQLException { - return executeQueryStatement( getSQLString(), queryParameters, scroll, session ); + return executeQueryStatement( getSQLString(), queryParameters, scroll, afterLoadActions, session ); } protected ResultSet executeQueryStatement( - final String sqlStatement, - final QueryParameters queryParameters, - final boolean scroll, - final SessionImplementor session) throws SQLException { + String sqlStatement, + QueryParameters queryParameters, + boolean scroll, + List afterLoadActions, + SessionImplementor session) throws SQLException { // Processing query filters. queryParameters.processFilters( sqlStatement, session ); @@ -1742,7 +1821,7 @@ public abstract class Loader { String sql = limitHandler.getProcessedSql(); // Adding locks and comments. - sql = preprocessSQL( sql, queryParameters, getFactory().getDialect() ); + sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions ); final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session ); return getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session ); @@ -2499,7 +2578,7 @@ public abstract class Loader { if ( stats ) startTime = System.currentTimeMillis(); try { - final ResultSet rs = executeQueryStatement( queryParameters, true, session ); + final ResultSet rs = executeQueryStatement( queryParameters, true, Collections.emptyList(), session ); final PreparedStatement st = (PreparedStatement) rs.getStatement(); if ( stats ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java index b01c50c7d0..d17125b896 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/collection/DynamicBatchingCollectionInitializerBuilder.java @@ -28,6 +28,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.hibernate.HibernateException; import org.hibernate.dialect.pagination.LimitHelper; @@ -250,10 +252,11 @@ public class DynamicBatchingCollectionInitializerBuilder extends BatchingCollect selection.getMaxRows() : Integer.MAX_VALUE; - final ResultSet rs = executeQueryStatement( sql, queryParameters, false, session ); + final List afterLoadActions = Collections.emptyList(); + final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session ); final Statement st = rs.getStatement(); try { - processResultSet( rs, queryParameters, session, true, null, maxRows ); + processResultSet( rs, queryParameters, session, true, null, maxRows, afterLoadActions ); } finally { st.close(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index 00d6ea820b..a33e926024 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -36,14 +36,19 @@ import org.hibernate.LockOptions; import org.hibernate.QueryException; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; +import org.hibernate.Session; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CriteriaImpl; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.OuterJoinLoader; +import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Lockable; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.transform.ResultTransformer; @@ -190,12 +195,55 @@ public class CriteriaLoader extends OuterJoinLoader { return querySpaces; } - protected String applyLocks(String sqlSelectString, LockOptions lockOptions, Dialect dialect) throws QueryException { + @Override + protected String applyLocks( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) throws QueryException { + final LockOptions lockOptions = parameters.getLockOptions(); if ( lockOptions == null || ( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) { - return sqlSelectString; + return sql; } + // user is request locking, lets see if we can apply locking directly to the SQL... + + // some dialects wont allow locking with paging... + final RowSelection rowSelection = parameters.getRowSelection(); + final LimitHandler limitHandler = dialect.buildLimitHandler( sql, rowSelection ); + if ( LimitHelper.useLimit( limitHandler, rowSelection ) ) { + // user has requested a combination of paging and locking. See if the dialect supports that + // (ahem, Oracle...) + if ( ! dialect.supportsLockingAndPaging() ) { + LOG.delayedLockingDueToPaging(); + + // this one is kind of ugly. currently we do not track the needed alias-to-entity + // mapping into the "hydratedEntities" which drives these callbacks + // so for now apply the root lock mode to all. The root lock mode is listed in + // the alias specific map under the alias "this_"... + final LockOptions lockOptionsToUse = new LockOptions(); + lockOptionsToUse.setLockMode( lockOptions.getEffectiveLockMode( "this_" ) ); + lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); + lockOptionsToUse.setScope( lockOptions.getScope() ); + + afterLoadActions.add( + new AfterLoadAction() { + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( lockOptionsToUse ) + .lock( persister.getEntityName(), entity ); + } + } + ); + parameters.setLockOptions( new LockOptions() ); + return sql; + } + } + + // there are other conditions we might want to add here, such as checking the result types etc + // but those are better served after we have redone the SQL generation to use ASTs. + final LockOptions locks = new LockOptions(lockOptions.getLockMode()); locks.setScope( lockOptions.getScope()); locks.setTimeOut( lockOptions.getTimeOut()); @@ -213,7 +261,7 @@ public class CriteriaLoader extends OuterJoinLoader { } } } - return dialect.applyLocksToSql( sqlSelectString, locks, keyColumnNames ); + return dialect.applyLocksToSql( sql, locks, keyColumnNames ); } protected LockMode[] getLockModes(LockOptions lockOptions) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java index c3b2418059..239451eea5 100755 --- a/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/custom/CustomLoader.java @@ -27,6 +27,7 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -38,7 +39,12 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryException; import org.hibernate.ScrollableResults; +import org.hibernate.Session; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.hql.internal.HolderInstantiator; @@ -50,6 +56,7 @@ 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.Lockable; import org.hibernate.persister.entity.Queryable; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.CollectionType; @@ -331,6 +338,35 @@ public class CustomLoader extends Loader { return list( session, queryParameters, querySpaces, resultTypes ); } + @Override + protected String applyLocks( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) throws QueryException { + final LockOptions lockOptions = parameters.getLockOptions(); + if ( lockOptions == null || + ( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) { + return sql; + } + + // user is request locking, lets see if we can apply locking directly to the SQL... + + // some dialects wont allow locking with paging... + afterLoadActions.add( + new AfterLoadAction() { + private final LockOptions originalLockOptions = lockOptions.makeCopy(); + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); + } + } + ); + parameters.getLockOptions().setLockMode( LockMode.READ ); + + return sql; + } + public ScrollableResults scroll( final QueryParameters queryParameters, final SessionImplementor session) throws HibernateException { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java index e8ae813567..f8b1faed12 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java @@ -27,6 +27,7 @@ import java.io.Serializable; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.ArrayList; import java.util.List; import org.jboss.logging.Logger; @@ -253,10 +254,11 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil selection.getMaxRows() : Integer.MAX_VALUE; - final ResultSet rs = executeQueryStatement( sql, queryParameters, false, session ); + final List afterLoadActions = new ArrayList(); + final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session ); final Statement st = rs.getStatement(); try { - return processResultSet( rs, queryParameters, session, false, null, maxRows ); + return processResultSet( rs, queryParameters, session, false, null, maxRows, afterLoadActions ); } finally { st.close(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java index 18ee1d5f95..a1c997390b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java @@ -23,10 +23,12 @@ */ package org.hibernate.loader.hql; +import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -37,8 +39,12 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryException; import org.hibernate.ScrollableResults; +import org.hibernate.Session; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.QueryParameters; +import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventSource; @@ -307,16 +313,35 @@ public class QueryLoader extends BasicLoader { return lockModesArray; } - protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException { + @Override + protected String applyLocks( + String sql, + QueryParameters parameters, + Dialect dialect, + List afterLoadActions) throws QueryException { // 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 LockOptions lockOptions = parameters.getLockOptions(); + if ( lockOptions == null || ( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) { return sql; } + + // user is request locking, lets see if we can apply locking directly to the SQL... + + // some dialects wont allow locking with paging... + if ( shouldDelayLockingDueToPaging( sql, parameters, dialect, afterLoadActions ) ) { + return sql; + } + + // there are other conditions we might want to add here, such as checking the result types etc + // but those are better served after we have redone the SQL generation to use ASTs. + + // we need both the set of locks and the columns to reference in locks // as the ultimate output of this section... final LockOptions locks = new LockOptions( lockOptions.getLockMode() ); @@ -490,7 +515,7 @@ public class QueryLoader extends BasicLoader { if ( queryParameters.isCallable() ) { throw new QueryException("iterate() not supported for callable statements"); } - final ResultSet rs = executeQueryStatement( queryParameters, false, session ); + final ResultSet rs = executeQueryStatement( queryParameters, false, Collections.emptyList(), session ); final PreparedStatement st = (PreparedStatement) rs.getStatement(); final Iterator result = new IteratorImpl( rs, diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java b/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java new file mode 100644 index 0000000000..bb0d833bf6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java @@ -0,0 +1,61 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.locking.paging; + +import javax.persistence.Entity; +import javax.persistence.Id; + +/** + * @author Steve Ebersole + */ +@Entity +public class Door { + private Integer id; + private String name; + + public Door() { + } + + public Door(Integer id, String name) { + this.id = id; + this.name = name; + } + + @Id + public Integer getId() { + return this.id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java b/hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java new file mode 100644 index 0000000000..d48e5e84d6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java @@ -0,0 +1,131 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.locking.paging; + +import java.util.List; + +import org.hibernate.Criteria; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.Query; +import org.hibernate.SQLQuery; +import org.hibernate.Session; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertEquals; + +/** + * Test of paging and locking in combination + * + * @author Steve Ebersole + */ +@TestForIssue( jiraKey = "HHH-1168" ) +public class PagingAndLockingTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Door.class }; + } + + @Before + public void createTestData() { + Session session = openSession(); + session.beginTransaction(); + session.save( new Door( 1, "Front" ) ); + session.save( new Door( 2, "Back" ) ); + session.save( new Door( 3, "Garage" ) ); + session.save( new Door( 4, "French" ) ); + session.getTransaction().commit(); + session.close(); + } + + @After + public void deleteTestData() { + Session session = openSession(); + session.beginTransaction(); + session.createQuery( "delete Door" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testHql() { + Session session = openSession(); + session.beginTransaction(); + Query qry = session.createQuery( "from Door" ); + qry.getLockOptions().setLockMode( LockMode.PESSIMISTIC_WRITE ); + qry.setFirstResult( 2 ); + qry.setMaxResults( 2 ); + @SuppressWarnings("unchecked") List results = qry.list(); + assertEquals( 2, results.size() ); + for ( Door door : results ) { + assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) ); + } + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testCriteria() { + Session session = openSession(); + session.beginTransaction(); + Criteria criteria = session.createCriteria( Door.class ); + criteria.setLockMode( LockMode.PESSIMISTIC_WRITE ); + criteria.setFirstResult( 2 ); + criteria.setMaxResults( 2 ); + @SuppressWarnings("unchecked") List results = criteria.list(); + assertEquals( 2, results.size() ); + for ( Door door : results ) { + assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) ); + } + session.getTransaction().commit(); + session.close(); + } + + @Test +// @Ignore( "Support for locking on native-sql queries not yet implemented" ) + public void testNativeSql() { + Session session = openSession(); + session.beginTransaction(); + SQLQuery qry = session.createSQLQuery( "select * from door" ); + qry.addRoot( "door", Door.class ); + qry.getLockOptions().setLockMode( LockMode.PESSIMISTIC_WRITE ); + qry.setFirstResult( 2 ); + qry.setMaxResults( 2 ); + @SuppressWarnings("unchecked") List results = qry.list(); + assertEquals( 2, results.size() ); + for ( Door door : results ) { + assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) ); + } + session.getTransaction().commit(); + session.close(); + } + +} diff --git a/hibernate-core/src/test/resources/hibernate.properties b/hibernate-core/src/test/resources/hibernate.properties index 0ea8ed3803..3d95e5b98e 100644 --- a/hibernate-core/src/test/resources/hibernate.properties +++ b/hibernate-core/src/test/resources/hibernate.properties @@ -20,7 +20,7 @@ # Free Software Foundation, Inc. # 51 Franklin Street, Fifth Floor # Boston, MA 02110-1301 USA -# + hibernate.dialect org.hibernate.dialect.H2Dialect hibernate.connection.driver_class org.h2.Driver hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java index 0a0813ea04..2b053dfa2e 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java @@ -53,6 +53,7 @@ import org.hibernate.engine.query.spi.NamedParameterDescriptor; import org.hibernate.engine.query.spi.OrdinalParameterDescriptor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.hql.internal.QueryExecutionRequestException; +import org.hibernate.internal.SQLQueryImpl; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.HibernateQuery; import org.hibernate.jpa.internal.util.ConfigurationHelper; @@ -242,12 +243,13 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, @Override protected boolean canApplyLockModes() { - return org.hibernate.internal.QueryImpl.class.isInstance( query ); + return org.hibernate.internal.QueryImpl.class.isInstance( query ) + || SQLQueryImpl.class.isInstance( query ); } @Override protected void applyAliasSpecificLockMode(String alias, LockMode lockMode) { - ( (org.hibernate.internal.QueryImpl) query ).getLockOptions().setAliasSpecificLockMode( alias, lockMode ); + query.getLockOptions().setAliasSpecificLockMode( alias, lockMode ); } /** @@ -631,18 +633,16 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, throw new IllegalStateException( "Not a JPAQL/Criteria query" ); } this.jpaLockMode = lockModeType; - ( (org.hibernate.internal.QueryImpl) query ).getLockOptions().setLockMode( - LockModeTypeHelper.getLockMode( lockModeType ) - ); - if ( getHints()!=null && getHints().containsKey( AvailableSettings.LOCK_TIMEOUT ) ) { - applyLockTimeout( ConfigurationHelper.getInteger( getHints().get( AvailableSettings.LOCK_TIMEOUT )) ); + query.getLockOptions().setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) ); + if ( getHints() != null && getHints().containsKey( AvailableSettings.LOCK_TIMEOUT ) ) { + applyLockTimeout( ConfigurationHelper.getInteger( getHints().get( AvailableSettings.LOCK_TIMEOUT ) ) ); } return this; } @Override protected void applyLockTimeout(int timeout) { - ( (org.hibernate.internal.QueryImpl) query ).getLockOptions().setTimeOut( timeout ); + query.getLockOptions().setTimeOut( timeout ); } @Override diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java index 4d2f089b2c..3dada08787 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java @@ -82,6 +82,32 @@ public class QueryLockingTest extends BaseEntityManagerFunctionalTestCase { em.close(); } + @Test + public void testNativeSql() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + QueryImpl query = em.createNativeQuery( "select * from lockable l" ).unwrap( QueryImpl.class ); + + org.hibernate.internal.SQLQueryImpl hibernateQuery = (org.hibernate.internal.SQLQueryImpl) query.getHibernateQuery(); +// assertEquals( LockMode.NONE, hibernateQuery.getLockOptions().getLockMode() ); +// assertNull( hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) ); +// assertEquals( LockMode.NONE, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) ); + + // NOTE : LockModeType.READ should map to LockMode.OPTIMISTIC + query.setLockMode( LockModeType.READ ); + assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getLockMode() ); + assertNull( hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) ); + assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) ); + + query.setHint( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE+".l", LockModeType.PESSIMISTIC_WRITE ); + assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getLockMode() ); + assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) ); + assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) ); + + em.getTransaction().commit(); + em.close(); + } + @Test public void testPessimisticForcedIncrementOverall() { EntityManager em = getOrCreateEntityManager(); From 5d2f21a01e939ed0f76b123aa12d74dc6c33b26b Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 20 Nov 2012 15:57:33 -0600 Subject: [PATCH 03/58] HHH-7816 - DDL opertations will incorrectly commit current transaction on Oracle XA --- .../hql/spi/TemporaryTableBulkIdStrategy.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/spi/TemporaryTableBulkIdStrategy.java b/hibernate-core/src/main/java/org/hibernate/hql/spi/TemporaryTableBulkIdStrategy.java index 3f118a9a3f..c77fcc43be 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/spi/TemporaryTableBulkIdStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/spi/TemporaryTableBulkIdStrategy.java @@ -105,7 +105,7 @@ public class TemporaryTableBulkIdStrategy implements MultiTableBulkIdStrategy { session.getTransactionCoordinator() .getTransaction() .createIsolationDelegate() - .delegateWork( work, session.getFactory().getSettings().isDataDefinitionInTransactionSupported() ); + .delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) ); } else { final Connection connection = session.getTransactionCoordinator() @@ -127,7 +127,7 @@ public class TemporaryTableBulkIdStrategy implements MultiTableBulkIdStrategy { session.getTransactionCoordinator() .getTransaction() .createIsolationDelegate() - .delegateWork( work, session.getFactory().getSettings().isDataDefinitionInTransactionSupported() ); + .delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) ); } else { final Connection connection = session.getTransactionCoordinator() @@ -174,6 +174,13 @@ public class TemporaryTableBulkIdStrategy implements MultiTableBulkIdStrategy { return session.getFactory().getSettings().isDataDefinitionImplicitCommit(); } + @SuppressWarnings({ "UnnecessaryUnboxing" }) + protected boolean shouldTransactIsolatedTemporaryTableDDL(SessionImplementor session) { + // is there ever a time when it makes sense to do this? +// return session.getFactory().getSettings().isDataDefinitionInTransactionSupported(); + return false; + } + private static class TemporaryTableCreationWork extends AbstractWork { private final Queryable persister; From 8df655aa6b2bbaa4d0766f194de638b5fdcf9a89 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 21 Nov 2012 12:53:13 -0500 Subject: [PATCH 04/58] HHH-7819 Correct test issues found in CI hibernate-core-master-matrix job --- .../test/annotations/enumerated/mapkey/User.java | 3 +++ .../hibernate/test/criterion/CriterionTest.java | 6 +++++- .../org/hibernate/test/hqlfetchscroll/Child.java | 11 +++++++++++ .../test/hqlfetchscroll/HQLScrollFetchTest.java | 2 +- .../hibernate/test/hqlfetchscroll/Parent.java | 13 ++++++++++++- .../test/hqlfetchscroll/ParentChild.hbm.xml | 16 ++++++++++++++-- 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/mapkey/User.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/mapkey/User.java index a286af8c18..f0ff936685 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/mapkey/User.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/mapkey/User.java @@ -30,6 +30,8 @@ import javax.persistence.FetchType; import javax.persistence.MapKeyColumn; import javax.persistence.MapKeyEnumerated; import javax.persistence.OneToMany; +import javax.persistence.Table; + import java.util.EnumMap; import java.util.Map; @@ -38,6 +40,7 @@ import java.util.Map; * @author Steve Ebersole */ @Entity +@Table( name = "USER_TABLE" ) public class User { @javax.persistence.Id @javax.persistence.GeneratedValue(generator = "system-uuid") diff --git a/hibernate-core/src/test/java/org/hibernate/test/criterion/CriterionTest.java b/hibernate-core/src/test/java/org/hibernate/test/criterion/CriterionTest.java index f95cca09aa..f501c81062 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/criterion/CriterionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/criterion/CriterionTest.java @@ -29,6 +29,7 @@ import org.hibernate.IrrelevantEntity; import org.hibernate.SessionFactory; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; import org.hibernate.criterion.CriteriaQuery; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.LikeExpression; @@ -42,6 +43,7 @@ import org.hibernate.type.Type; import org.junit.Test; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseUnitTestCase; import static org.junit.Assert.assertEquals; @@ -49,12 +51,13 @@ import static org.junit.Assert.assertEquals; /** * @author Steve Ebersole */ -public class CriterionTest extends BaseUnitTestCase { +public class CriterionTest extends BaseCoreFunctionalTestCase { @Test public void testIlikeRendering() { SessionFactory sf = new Configuration() .addAnnotatedClass( IrrelevantEntity.class ) .setProperty( AvailableSettings.DIALECT, IlikeSupportingDialect.class.getName() ) + .setProperty( Environment.HBM2DDL_AUTO, "create-drop" ) .buildSessionFactory(); final Criteria criteria = sf.openSession().createCriteria( IrrelevantEntity.class ); final CriteriaQueryTranslator translator = new CriteriaQueryTranslator( @@ -73,6 +76,7 @@ public class CriterionTest extends BaseUnitTestCase { SessionFactory sf = new Configuration() .addAnnotatedClass( IrrelevantEntity.class ) .setProperty( AvailableSettings.DIALECT, NonIlikeSupportingDialect.class.getName() ) + .setProperty( Environment.HBM2DDL_AUTO, "create-drop" ) .buildSessionFactory(); final Criteria criteria = sf.openSession().createCriteria( IrrelevantEntity.class ); final CriteriaQueryTranslator translator = new CriteriaQueryTranslator( diff --git a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Child.java b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Child.java index dfd7072816..54f6a9791d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Child.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Child.java @@ -2,6 +2,9 @@ package org.hibernate.test.hqlfetchscroll; public class Child { + // A numeric id must be the field. Some databases (Sybase, etc.) + // require identifier columns in order to support scrollable results. + private long id; private String name; Child() { @@ -11,6 +14,14 @@ public class Child { this.name = name; } + public long getId() { + return id; + } + + void setId(long id) { + this.id = id; + } + public String getName() { return name; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/HQLScrollFetchTest.java b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/HQLScrollFetchTest.java index 5b373219ad..ab2a026a42 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/HQLScrollFetchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/HQLScrollFetchTest.java @@ -176,8 +176,8 @@ public class HQLScrollFetchTest extends BaseCoreFunctionalTestCase { } } // check that the same second parent is obtained by calling Session.get() - assertSame( pOther, s.get( Parent.class, "parent2" ) ); assertNotNull( pOther ); + assertSame( pOther, s.get( Parent.class, pOther.getId() ) ); // access pOther's collection; should be completely loaded assertTrue( Hibernate.isInitialized( pOther.getChildren() ) ); assertEquals( childrenOther, pOther.getChildren() ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Parent.java b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Parent.java index 253b53cd4e..1ce74e3c5d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Parent.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/Parent.java @@ -4,6 +4,10 @@ import java.util.HashSet; import java.util.Set; public class Parent { + + // A numeric id must be the field. Some databases (Sybase, etc.) + // require identifier columns in order to support scrollable results. + private long id; private String name; private Set children = new HashSet(); @@ -14,11 +18,18 @@ public class Parent { this.name = name; } + public long getId() { + return id; + } + + void setId(long id) { + this.id = id; + } + public String getName() { return name; } - void setName(String name) { this.name = name; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/ParentChild.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/ParentChild.hbm.xml index 266c5b930a..3eda47a0ad 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/ParentChild.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/hqlfetchscroll/ParentChild.hbm.xml @@ -6,7 +6,13 @@ - + + + + + + @@ -16,7 +22,13 @@ - + + + + + + From 0118819133897e5355a0415de72fb1e39dcc67c0 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 21 Nov 2012 14:08:13 -0500 Subject: [PATCH 05/58] HHH-7819 Correct test issues found in CI hibernate-core-master-matrix job --- .../org/hibernate/test/propertyref/basic/Person.hbm.xml | 2 +- .../test/propertyref/component/complete/Mapping.hbm.xml | 2 +- .../test/propertyref/component/partial/Mapping.hbm.xml | 2 +- .../test/propertyref/inheritence/discrim/Person.hbm.xml | 8 ++++---- .../test/propertyref/inheritence/union/Person.hbm.xml | 8 ++++---- .../java/org/hibernate/test/readonly/DataPoint.hbm.xml | 2 +- .../hibernate/test/subclassfilter/union-subclass.hbm.xml | 2 +- .../test/java/org/hibernate/test/version/db/User.hbm.xml | 6 +++--- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml index 5e2b0d6f6f..e5f75d9a80 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/basic/Person.hbm.xml @@ -25,7 +25,7 @@ - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml index 8b7468c187..74b8ddda73 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/complete/Mapping.hbm.xml @@ -16,7 +16,7 @@ - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml index 004ebf4fb6..a2599d9825 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/component/partial/Mapping.hbm.xml @@ -16,7 +16,7 @@ - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml index 98fee61092..290d2c4c5f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/discrim/Person.hbm.xml @@ -11,10 +11,10 @@ - + - + @@ -22,8 +22,8 @@ - - + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml index 2df1413f32..54046df9cb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml @@ -10,10 +10,10 @@ - + - + @@ -21,8 +21,8 @@ - - + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml index 87e09522ac..0adee42df3 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml @@ -25,7 +25,7 @@ - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml index 2dfaa137f6..9b19b550fd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/subclassfilter/union-subclass.hbm.xml @@ -11,7 +11,7 @@ - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/version/db/User.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/version/db/User.hbm.xml index 994765a301..4dca4f9d8d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/version/db/User.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/test/version/db/User.hbm.xml @@ -17,7 +17,7 @@ - + @@ -34,7 +34,7 @@ - + @@ -47,7 +47,7 @@ - + From 54760bb61e9f34a432c1e57d11ebe21918530ed4 Mon Sep 17 00:00:00 2001 From: Csaba Varga Date: Wed, 21 Nov 2012 12:02:15 +0100 Subject: [PATCH 06/58] HHH-7800: added unit test to demonstrate broken behavior ("between" does not work inside an AuditDisjunction) --- .../test/integration/query/SimpleQuery.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java index dc55322d3d..613f27505f 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java @@ -34,6 +34,7 @@ import org.junit.Test; import org.hibernate.envers.enhanced.SequenceIdRevisionEntity; import org.hibernate.envers.RevisionType; import org.hibernate.envers.query.AuditEntity; +import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.Priority; import org.hibernate.envers.test.entities.StrIntTestEntity; @@ -325,4 +326,20 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase { List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 5).getResultList(); Assert.assertTrue(result.isEmpty()); } + + @Test + public void testBetweenInsideDisjunction() { + List result = getAuditReader().createQuery() + .forRevisionsOfEntity(StrIntTestEntity.class, true, true) + .add(AuditEntity.disjunction() + .add(AuditEntity.property("number").between(0, 5)) + .add(AuditEntity.property("number").between(20, 100))) + .getResultList(); + + for (Object o : result) { + StrIntTestEntity entity = (StrIntTestEntity)o; + int number = entity.getNumber(); + assert ( number >= 0 && number <= 5) || ( number >= 20 && number <= 100); + } + } } From ff25434d1f6d48b35d58cdb0305f11504c498d2c Mon Sep 17 00:00:00 2001 From: Csaba Varga Date: Wed, 21 Nov 2012 12:12:17 +0100 Subject: [PATCH 07/58] HHH-7800: Fixed "between" doesn't work inside a disjunction issue --- .../envers/query/criteria/BetweenAuditExpression.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/BetweenAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/BetweenAuditExpression.java index bec0a534be..c2b289f71d 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/BetweenAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/BetweenAuditExpression.java @@ -44,7 +44,9 @@ public class BetweenAuditExpression implements AuditCriterion { public void addToQuery(AuditConfiguration auditCfg, String entityName, QueryBuilder qb, Parameters parameters) { String propertyName = propertyNameGetter.get(auditCfg); CriteriaTools.checkPropertyNotARelation(auditCfg, entityName, propertyName); - parameters.addWhereWithParam(propertyName, ">=", lo); - parameters.addWhereWithParam(propertyName, "<=", hi); + + Parameters subParams = parameters.addSubParameters(Parameters.AND); + subParams.addWhereWithParam(propertyName, ">=", lo); + subParams.addWhereWithParam(propertyName, "<=", hi); } } From cd3363151b77225458e37816f74c537cbc4798f6 Mon Sep 17 00:00:00 2001 From: Csaba Varga Date: Wed, 21 Nov 2012 13:38:32 +0100 Subject: [PATCH 08/58] HHH-7800: added unit test to demonstrate broken behavior ("maximize" does not work inside an AuditDisjunction) --- .../query/MaximalizePropertyQuery.java | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java index b4e820031f..f4358a9d45 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java @@ -24,15 +24,20 @@ package org.hibernate.envers.test.integration.query; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; + import javax.persistence.EntityManager; -import org.junit.Test; +import junit.framework.Assert; import org.hibernate.envers.query.AuditEntity; +import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.Priority; import org.hibernate.envers.test.entities.StrIntTestEntity; +import org.junit.Test; /** * @author Adam Warski (adam at warski dot org) @@ -41,6 +46,7 @@ import org.hibernate.envers.test.entities.StrIntTestEntity; public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { Integer id1; Integer id2; + Integer id3; @Override protected Class[] getAnnotatedClasses() { @@ -56,12 +62,15 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { StrIntTestEntity site1 = new StrIntTestEntity("a", 10); StrIntTestEntity site2 = new StrIntTestEntity("b", 15); + StrIntTestEntity site3 = new StrIntTestEntity("c", 42); em.persist(site1); em.persist(site2); + em.persist(site3); id1 = site1.getId(); id2 = site2.getId(); + id3 = site3.getId(); em.getTransaction().commit(); @@ -135,4 +144,30 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { System.out.println(result); assert Arrays.asList(2).equals(result); } + + @Test + public void testMaximizeInDisjunction() { + List idsToQuery = Arrays.asList(id1, id3); + + AuditDisjunction disjunction = AuditEntity.disjunction(); + + for (Integer id : idsToQuery) { + disjunction.add(AuditEntity.revisionNumber().maximize() + .add(AuditEntity.id().eq(id))); + } + List result = getAuditReader().createQuery() + .forRevisionsOfEntity(StrIntTestEntity.class, true, true) + .add(disjunction) + .getResultList(); + + Set idsSeen = new HashSet(); + for (Object o : result) { + StrIntTestEntity entity = (StrIntTestEntity) o; + Integer id = entity.getId(); + Assert.assertTrue("Entity with ID "+id+" returned but not queried for.", idsToQuery.contains(id)); + if (!idsSeen.add(id)) { + Assert.fail("Multiple revisions returned with ID "+id+"; expected only one."); + } + } + } } \ No newline at end of file From dadc43af23b1e8dd8623abc66a9ff6864993bc14 Mon Sep 17 00:00:00 2001 From: Csaba Varga Date: Wed, 21 Nov 2012 14:30:07 +0100 Subject: [PATCH 09/58] HHH-7800: Fixed "maximize" doesn't work inside a disjunction issue --- .../envers/query/criteria/AggregatedAuditExpression.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java index 4be8b5d617..1f4003e9d8 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java @@ -59,13 +59,15 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit CriteriaTools.checkPropertyNotARelation(auditCfg, entityName, propertyName); + // Make sure our conditions are ANDed together even if the parent Parameters have a different connective + Parameters subParams = parameters.addSubParameters(Parameters.AND); // This will be the aggregated query, containing all the specified conditions QueryBuilder subQb = qb.newSubQueryBuilder(); // Adding all specified conditions both to the main query, as well as to the // aggregated one. for (AuditCriterion versionsCriteria : criterions) { - versionsCriteria.addToQuery(auditCfg, entityName, qb, parameters); + versionsCriteria.addToQuery(auditCfg, entityName, qb, subParams); versionsCriteria.addToQuery(auditCfg, entityName, subQb, subQb.getRootParameters()); } @@ -79,6 +81,6 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit } // Adding the constrain on the result of the aggregated criteria - parameters.addWhere(propertyName, "=", subQb); + subParams.addWhere(propertyName, "=", subQb); } } \ No newline at end of file From 468c58358edbf091e33642fa123ac3494d67738d Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Thu, 22 Nov 2012 12:01:47 +0100 Subject: [PATCH 10/58] HHH-7800 - Tests cleanup --- .../query/MaximalizePropertyQuery.java | 58 ++++----- .../test/integration/query/SimpleQuery.java | 35 +++--- .../integration/query/StoreDeletedData.java | 112 +++++++++++++----- 3 files changed, 132 insertions(+), 73 deletions(-) diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java index f4358a9d45..01c999273e 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java @@ -30,13 +30,14 @@ import java.util.Set; import javax.persistence.EntityManager; -import junit.framework.Assert; - import org.hibernate.envers.query.AuditEntity; import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.Priority; import org.hibernate.envers.test.entities.StrIntTestEntity; +import org.hibernate.testing.TestForIssue; + +import org.junit.Assert; import org.junit.Test; /** @@ -141,33 +142,32 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { .add(AuditEntity.property("number").eq(10))) .getResultList(); - System.out.println(result); assert Arrays.asList(2).equals(result); } - - @Test - public void testMaximizeInDisjunction() { - List idsToQuery = Arrays.asList(id1, id3); - - AuditDisjunction disjunction = AuditEntity.disjunction(); - - for (Integer id : idsToQuery) { - disjunction.add(AuditEntity.revisionNumber().maximize() - .add(AuditEntity.id().eq(id))); - } - List result = getAuditReader().createQuery() - .forRevisionsOfEntity(StrIntTestEntity.class, true, true) - .add(disjunction) - .getResultList(); - - Set idsSeen = new HashSet(); - for (Object o : result) { - StrIntTestEntity entity = (StrIntTestEntity) o; - Integer id = entity.getId(); - Assert.assertTrue("Entity with ID "+id+" returned but not queried for.", idsToQuery.contains(id)); - if (!idsSeen.add(id)) { - Assert.fail("Multiple revisions returned with ID "+id+"; expected only one."); - } - } - } + + @Test + @TestForIssue(jiraKey = "HHH-7800") + public void testMaximizeInDisjunction() { + List idsToQuery = Arrays.asList( id1, id3 ); + + AuditDisjunction disjunction = AuditEntity.disjunction(); + + for ( Integer id : idsToQuery ) { + disjunction.add( AuditEntity.revisionNumber().maximize().add( AuditEntity.id().eq( id ) ) ); + } + List result = getAuditReader().createQuery() + .forRevisionsOfEntity( StrIntTestEntity.class, true, true ) + .add( disjunction ) + .getResultList(); + + Set idsSeen = new HashSet(); + for ( Object o : result ) { + StrIntTestEntity entity = (StrIntTestEntity) o; + Integer id = entity.getId(); + Assert.assertTrue( "Entity with ID " + id + " returned but not queried for.", idsToQuery.contains( id ) ); + if ( !idsSeen.add( id ) ) { + Assert.fail( "Multiple revisions returned with ID " + id + "; expected only one." ); + } + } + } } \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java index 613f27505f..c3515dec45 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/SimpleQuery.java @@ -34,11 +34,11 @@ import org.junit.Test; import org.hibernate.envers.enhanced.SequenceIdRevisionEntity; import org.hibernate.envers.RevisionType; import org.hibernate.envers.query.AuditEntity; -import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.Priority; import org.hibernate.envers.test.entities.StrIntTestEntity; import org.hibernate.envers.test.tools.TestTools; +import org.hibernate.testing.TestForIssue; /** * @author Adam Warski (adam at warski dot org) @@ -326,20 +326,21 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase { List result = getAuditReader().createQuery().forEntitiesModifiedAtRevision(StrIntTestEntity.class, 5).getResultList(); Assert.assertTrue(result.isEmpty()); } - - @Test - public void testBetweenInsideDisjunction() { - List result = getAuditReader().createQuery() - .forRevisionsOfEntity(StrIntTestEntity.class, true, true) - .add(AuditEntity.disjunction() - .add(AuditEntity.property("number").between(0, 5)) - .add(AuditEntity.property("number").between(20, 100))) - .getResultList(); - - for (Object o : result) { - StrIntTestEntity entity = (StrIntTestEntity)o; - int number = entity.getNumber(); - assert ( number >= 0 && number <= 5) || ( number >= 20 && number <= 100); - } - } + + @Test + @TestForIssue(jiraKey = "HHH-7800") + public void testBetweenInsideDisjunction() { + List result = getAuditReader().createQuery() + .forRevisionsOfEntity( StrIntTestEntity.class, true, true ) + .add( AuditEntity.disjunction() + .add( AuditEntity.property( "number" ).between( 0, 5 ) ) + .add( AuditEntity.property( "number" ).between( 20, 100 ) ) ) + .getResultList(); + + for ( Object o : result ) { + StrIntTestEntity entity = (StrIntTestEntity) o; + int number = entity.getNumber(); + Assert.assertTrue( ( number >= 0 && number <= 5 ) || ( number >= 20 && number <= 100 ) ); + } + } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/StoreDeletedData.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/StoreDeletedData.java index cbb1f32bc2..6e9d3763c7 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/StoreDeletedData.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/StoreDeletedData.java @@ -23,29 +23,38 @@ */ package org.hibernate.envers.test.integration.query; +import java.util.Arrays; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; +import junit.framework.Assert; import org.junit.Test; +import org.hibernate.envers.RevisionType; +import org.hibernate.envers.enhanced.SequenceIdRevisionEntity; import org.hibernate.envers.query.AuditEntity; +import org.hibernate.envers.query.criteria.AuditCriterion; +import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.Priority; import org.hibernate.envers.test.entities.StrIntTestEntity; +import org.hibernate.testing.TestForIssue; /** * A test which checks if the data of a deleted entity is stored when the setting is on. * @author Adam Warski (adam at warski dot org) */ -@SuppressWarnings({"unchecked"}) +@SuppressWarnings({ "unchecked" }) public class StoreDeletedData extends BaseEnversJPAFunctionalTestCase { - private Integer id1; + private Integer id1; + private Integer id2; + private Integer id3; @Override protected Class[] getAnnotatedClasses() { return new Class[] { StrIntTestEntity.class }; - } + } @Override protected void addConfigOptions(Map options) { @@ -54,37 +63,86 @@ public class StoreDeletedData extends BaseEnversJPAFunctionalTestCase { } @Test - @Priority(10) - public void initData() { - // Revision 1 - EntityManager em = getEntityManager(); - em.getTransaction().begin(); + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); - StrIntTestEntity site1 = new StrIntTestEntity("a", 10); + // Revision 1 + em.getTransaction().begin(); + StrIntTestEntity site1 = new StrIntTestEntity( "a", 10 ); + em.persist( site1 ); + id1 = site1.getId(); + em.getTransaction().commit(); - em.persist(site1); + // Revision 2 + em.getTransaction().begin(); + em.remove( site1 ); + em.getTransaction().commit(); - id1 = site1.getId(); + // Revision 3 + em.getTransaction().begin(); + StrIntTestEntity site2 = new StrIntTestEntity( "b", 20 ); + em.persist( site2 ); + id2 = site2.getId(); + StrIntTestEntity site3 = new StrIntTestEntity( "c", 30 ); + em.persist( site3 ); + id3 = site3.getId(); + em.getTransaction().commit(); - em.getTransaction().commit(); + // Revision 4 + em.getTransaction().begin(); + em.remove( site2 ); + em.remove( site3 ); + em.getTransaction().commit(); - // Revision 2 - em.getTransaction().begin(); + em.close(); + } - em.remove(site1); + @Test + public void testRevisionsPropertyEqQuery() { + List revs_id1 = getAuditReader().createQuery() + .forRevisionsOfEntity( StrIntTestEntity.class, false, true ) + .add( AuditEntity.id().eq( id1 ) ) + .getResultList(); - em.getTransaction().commit(); - } + Assert.assertEquals( 2, revs_id1.size() ); + Assert.assertEquals( new StrIntTestEntity( "a", 10, id1 ), ( (Object[]) revs_id1.get( 0 ) )[0] ); + Assert.assertEquals( new StrIntTestEntity( "a", 10, id1 ), ( (Object[]) revs_id1.get( 1 ) )[0] ); + } - @Test - public void testRevisionsPropertyEqQuery() { - List revs_id1 = getAuditReader().createQuery() - .forRevisionsOfEntity(StrIntTestEntity.class, false, true) - .add(AuditEntity.id().eq(id1)) - .getResultList(); + @Test + @TestForIssue(jiraKey = "HHH-7800") + public void testMaximizeInDisjunction() { + List queryIds = Arrays.asList( id2, id3 ); - assert revs_id1.size() == 2; - assert ((Object[]) revs_id1.get(0))[0].equals(new StrIntTestEntity("a", 10, id1)); - assert ((Object[]) revs_id1.get(1))[0].equals(new StrIntTestEntity("a", 10, id1)); - } + AuditDisjunction disjunction = AuditEntity.disjunction(); + for ( Integer id : queryIds ) { + AuditCriterion crit = AuditEntity.revisionNumber().maximize() + .add( AuditEntity.id().eq( id ) ) + .add( AuditEntity.revisionType().ne( RevisionType.DEL ) ); + disjunction.add( crit ); + // Workaround: using this line instead works correctly: + // disjunction.add(AuditEntity.conjunction().add(crit)); + } + + List beforeDeletionRevisions = getAuditReader().createQuery() + .forRevisionsOfEntity( StrIntTestEntity.class, false, false ) + .add( disjunction ) + .addOrder( AuditEntity.property( "id" ).asc() ) + .getResultList(); + + Assert.assertEquals( 2, beforeDeletionRevisions.size() ); + + Object[] result1 = (Object[]) beforeDeletionRevisions.get( 0 ); + Object[] result2 = (Object[]) beforeDeletionRevisions.get( 1 ); + + Assert.assertEquals( new StrIntTestEntity( "b", 20, id2 ), result1[0] ); + // Making sure that we have received an entity added at revision 3. + Assert.assertEquals( 3, ( (SequenceIdRevisionEntity) result1[1] ).getId() ); + Assert.assertEquals( RevisionType.ADD, result1[2] ); + Assert.assertEquals( new StrIntTestEntity( "c", 30, id3 ), result2[0] ); + // Making sure that we have received an entity added at revision 3. + Assert.assertEquals( 3, ( (SequenceIdRevisionEntity) result2[1] ).getId() ); + Assert.assertEquals( RevisionType.ADD, result2[2] ); + } } \ No newline at end of file From 14d1c626a5b3e678535aa23f689cadb0a0c34fd9 Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Sat, 24 Nov 2012 18:08:15 +0100 Subject: [PATCH 11/58] HHH-7612 - Fix and test --- .../java/org/hibernate/mapping/Table.java | 32 +++++++++++++++-- .../primarykey/NullablePrimaryKeyTest.java | 4 +-- .../org/hibernate/test/propertyref/import.sql | 2 +- .../schemaupdate/SchemaGenerationTest.java | 36 +++++++++++++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 3b34475107..e3825b0c6c 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -457,7 +458,7 @@ public class Table implements RelationalModel, Serializable { .append( ' ' ) .append( name ) .append( " (" ); - Iterator itr = getColumnIterator(); + Iterator itr = getSortedColumnIterator(); while ( itr.hasNext() ) { final Column column = (Column) itr.next(); buffer.append( column.getQuotedName( dialect ) ).append( ' ' ); @@ -491,7 +492,7 @@ public class Table implements RelationalModel, Serializable { pkname = ( (Column) getPrimaryKey().getColumnIterator().next() ).getQuotedName( dialect ); } - Iterator iter = getColumnIterator(); + Iterator iter = getSortedColumnIterator(); while ( iter.hasNext() ) { Column col = (Column) iter.next(); @@ -591,6 +592,33 @@ public class Table implements RelationalModel, Serializable { return buf.append( dialect.getTableTypeString() ).toString(); } + /** + * @return Sorted column list so that primary key appears first, followed by foreign keys and other properties. + * Within each group columns are not sorted in any way. + */ + private Iterator getSortedColumnIterator() { + final LinkedHashSet sortedColumns = new LinkedHashSet(); + // Adding primary key columns. + if ( hasPrimaryKey() ) { + sortedColumns.addAll( getPrimaryKey().getColumns() ); + } + // Adding foreign key columns. + Iterator iter = getForeignKeyIterator(); + while ( iter.hasNext() ) { + ForeignKey fk = (ForeignKey) iter.next(); + sortedColumns.addAll( fk.getColumns() ); + } + // Adding other columns. + iter = getColumnIterator(); + while ( iter.hasNext() ) { + final Column column = (Column) iter.next(); + if ( ! sortedColumns.contains( column ) ) { + sortedColumns.add( column ); + } + } + return sortedColumns.iterator(); + } + public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) { StringBuilder buf = new StringBuilder( "drop table " ); if ( dialect.supportsIfExistsBeforeTableName() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java index 27325d900b..f5ee380e2d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java @@ -34,8 +34,8 @@ public class NullablePrimaryKeyTest { for (String s : schema) { log.debug(s); } - String expectedMappingTableSql = "create table personAddress (address_id numeric(19,0), " + - "person_id numeric(19,0) not null, primary key (person_id))"; + String expectedMappingTableSql = "create table personAddress (person_id numeric(19,0) not null, " + + "address_id numeric(19,0), primary key (person_id))"; Assert.assertEquals( "Wrong SQL", expectedMappingTableSql, schema[2] ); } catch (Exception e) { Assert.fail(e.getMessage()); diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql b/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql index b9d3a08744..19f76aa056 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql @@ -1,3 +1,3 @@ INSERT INTO `vgras007_v031` VALUES ('ZZZ','00',1); -INSERT INTO `vgras029_v031` VALUES (1,'Foo Foo Foo',1), (1,'Bar Bar Bar',2); \ No newline at end of file +INSERT INTO `vgras029_v031` VALUES (1,1,'Foo Foo Foo'), (1,2,'Bar Bar Bar'); \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java new file mode 100644 index 0000000000..c8ea44ae66 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java @@ -0,0 +1,36 @@ +package org.hibernate.test.schemaupdate; + +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.spi.Mapping; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.test.onetomany.Node; +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class SchemaGenerationTest extends BaseCoreFunctionalTestCase { + @Override + protected String[] getMappings() { + return new String[] { "onetomany/Node.hbm.xml" }; + } + + @Test + @TestForIssue( jiraKey = "HHH-7612" ) + @RequiresDialect( H2Dialect.class ) + public void testSqlCreatePrimaryAndForeignKeyOrder() { + final Mapping mappings = configuration().buildMapping(); + final PersistentClass persistentClass = configuration().getClassMapping( Node.class.getName() ); + final String sqlCreate = persistentClass.getTable().sqlCreateString( getDialect(), mappings, null, null ); + Assert.assertEquals( + "PK and FK columns should appear first in CREATE TABLE statement.", + "create table Node (id integer not null, node_id integer, description varchar(255), idx integer, primary key (id))", + sqlCreate + ); + } +} From 4785d7eb2e63f6d692908e84e29f64fa62b9a1a1 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 26 Nov 2012 10:34:59 -0600 Subject: [PATCH 12/58] HHH-7233 - unmuck EntityManager#getSingleResult wrt auto-setting of max results --- .../org/hibernate/jpa/internal/QueryImpl.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java index 2b053dfa2e..80deff020e 100755 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/QueryImpl.java @@ -277,19 +277,7 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, @SuppressWarnings({ "unchecked", "RedundantCast" }) public X getSingleResult() { try { - boolean mucked = false; - // IMPL NOTE : the mucking with max results here is attempting to help the user from shooting themselves - // in the foot in the case where they have a large query by limiting the query results to 2 max - // SQLQuery cannot be safely paginated, leaving the user's choice here. - if ( getSpecifiedMaxResults() != 1 && - ! ( SQLQuery.class.isAssignableFrom( query.getClass() ) ) ) { - mucked = true; - query.setMaxResults( 2 ); //avoid OOME if the list is huge - } - List result = query.list(); - if ( mucked ) { - query.setMaxResults( getSpecifiedMaxResults() ); - } + final List result = query.list(); if ( result.size() == 0 ) { NoResultException nre = new NoResultException( "No entity found for query" ); @@ -297,7 +285,7 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, throw nre; } else if ( result.size() > 1 ) { - Set uniqueResult = new HashSet(result); + final Set uniqueResult = new HashSet(result); if ( uniqueResult.size() > 1 ) { NonUniqueResultException nure = new NonUniqueResultException( "result returns more than one elements" ); getEntityManager().handlePersistenceException( nure ); @@ -306,7 +294,6 @@ public class QueryImpl extends AbstractQueryImpl implements TypedQuery, else { return uniqueResult.iterator().next(); } - } else { return result.get( 0 ); From dd280b8c27864be2699c0c1a427a70a6d9084d98 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 26 Nov 2012 13:19:08 -0600 Subject: [PATCH 13/58] HHH-1168 - Problem combining locking and paging on Oracle --- .../src/test/java/org/hibernate/test/locking/paging/Door.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java b/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java index bb0d833bf6..6cfbb3cc99 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java +++ b/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java @@ -25,11 +25,13 @@ package org.hibernate.test.locking.paging; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Table; /** * @author Steve Ebersole */ @Entity +@Table( name = "door" ) public class Door { private Integer id; private String name; From caf2ee420c171ad23a4745a6777cf2694de72a5d Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 26 Nov 2012 17:49:55 -0600 Subject: [PATCH 14/58] HHH-7825 - org.hibernate.type.descriptor.java.DataHelper is incompatible with FireBird JDBC --- .../type/descriptor/java/DataHelper.java | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DataHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DataHelper.java index 8a17e8a185..60f977bcde 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DataHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DataHelper.java @@ -30,6 +30,7 @@ import java.io.Reader; import java.io.StringReader; import java.sql.Clob; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import org.jboss.logging.Logger; @@ -268,26 +269,41 @@ public class DataHelper { /** * Extract the contents of the given Clob as a string. * - * @param reader The reader for the content + * @param value The clob to to be extracted from * * @return The content as string */ public static String extractString(final Clob value) { try { - Reader characterStream = value.getCharacterStream(); - long length = value.length(); - if ( length > Integer.MAX_VALUE ) { - return extractString( characterStream, Integer.MAX_VALUE ); - } - else { - return extractString( characterStream, (int) length ); - } + final Reader characterStream = value.getCharacterStream(); + final long length = determineLengthForBufferSizing( value ); + return length > Integer.MAX_VALUE + ? extractString( characterStream, Integer.MAX_VALUE ) + : extractString( characterStream, (int) length ); } catch ( SQLException e ) { throw new HibernateException( "Unable to access lob stream", e ); } } + /** + * Determine a buffer size for reading the underlying character stream. + * + * @param value The Clob value + * + * @return The appropriate buffer size ({@link java.sql.Clob#length()} by default. + * + * @throws SQLException + */ + private static long determineLengthForBufferSizing(Clob value) throws SQLException { + try { + return value.length(); + } + catch ( SQLFeatureNotSupportedException e ) { + return BUFFER_SIZE; + } + } + /** * Make sure we allocate a buffer sized not bigger than 2048, * not higher than what is actually needed, and at least one. @@ -295,7 +311,7 @@ public class DataHelper { * @param lengthHint the expected size of the full value * @return the buffer size */ - private static final int getSuggestedBufferSize(final int lengthHint) { + private static int getSuggestedBufferSize(final int lengthHint) { return Math.max( 1, Math.min( lengthHint , BUFFER_SIZE ) ); } } From 5707798b4b4a3a7b99fc47faf5575ec11be2c4e0 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Wed, 28 Nov 2012 23:35:08 +0800 Subject: [PATCH 15/58] Revert "HHH-7612 - Fix and test" This reverts commit 14d1c626a5b3e678535aa23f689cadb0a0c34fd9. --- .../java/org/hibernate/mapping/Table.java | 32 ++--------------- .../primarykey/NullablePrimaryKeyTest.java | 4 +-- .../org/hibernate/test/propertyref/import.sql | 2 +- .../schemaupdate/SchemaGenerationTest.java | 36 ------------------- 4 files changed, 5 insertions(+), 69 deletions(-) delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index e3825b0c6c..3b34475107 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -29,7 +29,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -458,7 +457,7 @@ public class Table implements RelationalModel, Serializable { .append( ' ' ) .append( name ) .append( " (" ); - Iterator itr = getSortedColumnIterator(); + Iterator itr = getColumnIterator(); while ( itr.hasNext() ) { final Column column = (Column) itr.next(); buffer.append( column.getQuotedName( dialect ) ).append( ' ' ); @@ -492,7 +491,7 @@ public class Table implements RelationalModel, Serializable { pkname = ( (Column) getPrimaryKey().getColumnIterator().next() ).getQuotedName( dialect ); } - Iterator iter = getSortedColumnIterator(); + Iterator iter = getColumnIterator(); while ( iter.hasNext() ) { Column col = (Column) iter.next(); @@ -592,33 +591,6 @@ public class Table implements RelationalModel, Serializable { return buf.append( dialect.getTableTypeString() ).toString(); } - /** - * @return Sorted column list so that primary key appears first, followed by foreign keys and other properties. - * Within each group columns are not sorted in any way. - */ - private Iterator getSortedColumnIterator() { - final LinkedHashSet sortedColumns = new LinkedHashSet(); - // Adding primary key columns. - if ( hasPrimaryKey() ) { - sortedColumns.addAll( getPrimaryKey().getColumns() ); - } - // Adding foreign key columns. - Iterator iter = getForeignKeyIterator(); - while ( iter.hasNext() ) { - ForeignKey fk = (ForeignKey) iter.next(); - sortedColumns.addAll( fk.getColumns() ); - } - // Adding other columns. - iter = getColumnIterator(); - while ( iter.hasNext() ) { - final Column column = (Column) iter.next(); - if ( ! sortedColumns.contains( column ) ) { - sortedColumns.add( column ); - } - } - return sortedColumns.iterator(); - } - public String sqlDropString(Dialect dialect, String defaultCatalog, String defaultSchema) { StringBuilder buf = new StringBuilder( "drop table " ); if ( dialect.supportsIfExistsBeforeTableName() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java index f5ee380e2d..27325d900b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/onetoone/primarykey/NullablePrimaryKeyTest.java @@ -34,8 +34,8 @@ public class NullablePrimaryKeyTest { for (String s : schema) { log.debug(s); } - String expectedMappingTableSql = "create table personAddress (person_id numeric(19,0) not null, " + - "address_id numeric(19,0), primary key (person_id))"; + String expectedMappingTableSql = "create table personAddress (address_id numeric(19,0), " + + "person_id numeric(19,0) not null, primary key (person_id))"; Assert.assertEquals( "Wrong SQL", expectedMappingTableSql, schema[2] ); } catch (Exception e) { Assert.fail(e.getMessage()); diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql b/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql index 19f76aa056..b9d3a08744 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql @@ -1,3 +1,3 @@ INSERT INTO `vgras007_v031` VALUES ('ZZZ','00',1); -INSERT INTO `vgras029_v031` VALUES (1,1,'Foo Foo Foo'), (1,2,'Bar Bar Bar'); \ No newline at end of file +INSERT INTO `vgras029_v031` VALUES (1,'Foo Foo Foo',1), (1,'Bar Bar Bar',2); \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java deleted file mode 100644 index c8ea44ae66..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaGenerationTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.hibernate.test.schemaupdate; - -import org.junit.Assert; -import org.junit.Test; - -import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.spi.Mapping; -import org.hibernate.mapping.PersistentClass; -import org.hibernate.test.onetomany.Node; -import org.hibernate.testing.RequiresDialect; -import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; - -/** - * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) - */ -public class SchemaGenerationTest extends BaseCoreFunctionalTestCase { - @Override - protected String[] getMappings() { - return new String[] { "onetomany/Node.hbm.xml" }; - } - - @Test - @TestForIssue( jiraKey = "HHH-7612" ) - @RequiresDialect( H2Dialect.class ) - public void testSqlCreatePrimaryAndForeignKeyOrder() { - final Mapping mappings = configuration().buildMapping(); - final PersistentClass persistentClass = configuration().getClassMapping( Node.class.getName() ); - final String sqlCreate = persistentClass.getTable().sqlCreateString( getDialect(), mappings, null, null ); - Assert.assertEquals( - "PK and FK columns should appear first in CREATE TABLE statement.", - "create table Node (id integer not null, node_id integer, description varchar(255), idx integer, primary key (id))", - sqlCreate - ); - } -} From 6e71a0907eba1a83ef50f3f6d4bf860c7df292e5 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 29 Nov 2012 12:33:54 -0600 Subject: [PATCH 16/58] HHH-1168 - Problem combining locking and paging on Oracle --- .../java/org/hibernate/dialect/Dialect.java | 4 +- .../hibernate/dialect/Oracle8iDialect.java | 4 +- .../hibernate/internal/CoreMessageLogger.java | 6 +-- .../java/org/hibernate/loader/Loader.java | 33 +++++------- .../loader/criteria/CriteriaLoader.java | 53 ++++++------------- .../org/hibernate/loader/hql/QueryLoader.java | 7 +-- 6 files changed, 38 insertions(+), 69 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 303307caaf..ce2aa4653f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2400,7 +2400,7 @@ public abstract class Dialect implements ConversionContext { return false; } - public boolean supportsLockingAndPaging() { - return true; + public boolean useFollowOnLocking() { + return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java index d18d13cfcf..faa634c637 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java @@ -578,7 +578,7 @@ public class Oracle8iDialect extends Dialect { } @Override - public boolean supportsLockingAndPaging() { - return false; + public boolean useFollowOnLocking() { + return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 88f1dab89d..20fce8b934 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1586,9 +1586,9 @@ public interface CoreMessageLogger extends BasicLogger { @LogMessage(level = WARN) @Message( - value = "Encountered request which combined locking and paging, however dialect reports that database does " + - "not support that combination. Results will be locked after initial query executes", + value = "Encountered request for locking however dialect reports that database prefers locking be done in a " + + "separate select; results will be locked after initial query executes", id = 444 ) - void delayedLockingDueToPaging(); + void usingFollowOnLocking(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 3d061567fc..67d1907e49 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -249,30 +249,23 @@ public abstract class Loader { public void afterLoad(SessionImplementor session, Object entity, Loadable persister); } - protected boolean shouldDelayLockingDueToPaging( - String sql, + protected boolean shouldUseFollowOnLocking( QueryParameters parameters, Dialect dialect, List afterLoadActions) { - final LockOptions lockOptions = parameters.getLockOptions(); - final RowSelection rowSelection = parameters.getRowSelection(); - final LimitHandler limitHandler = dialect.buildLimitHandler( sql, rowSelection ); - if ( LimitHelper.useLimit( limitHandler, rowSelection ) ) { - // user has requested a combination of paging and locking. See if the dialect supports that - // (ahem, Oracle...) - if ( ! dialect.supportsLockingAndPaging() ) { - LOG.delayedLockingDueToPaging(); - afterLoadActions.add( - new AfterLoadAction() { - private final LockOptions originalLockOptions = lockOptions.makeCopy(); - @Override - public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { - ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); - } + if ( dialect.useFollowOnLocking() ) { + LOG.usingFollowOnLocking(); + final LockOptions lockOptions = parameters.getLockOptions(); + afterLoadActions.add( + new AfterLoadAction() { + private final LockOptions originalLockOptions = lockOptions.makeCopy(); + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); } - ); - parameters.setLockOptions( new LockOptions() ); - } + } + ); + parameters.setLockOptions( new LockOptions() ); return true; } return false; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index a33e926024..c5f0add9a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -38,11 +38,8 @@ import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; -import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CriteriaImpl; @@ -207,43 +204,27 @@ public class CriteriaLoader extends OuterJoinLoader { return sql; } - // user is request locking, lets see if we can apply locking directly to the SQL... + if ( dialect.useFollowOnLocking() ) { + // Dialect prefers to perform locking in a separate step + LOG.usingFollowOnLocking(); + final LockOptions lockOptionsToUse = new LockOptions(); + lockOptionsToUse.setLockMode( lockOptions.getEffectiveLockMode( "this_" ) ); + lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); + lockOptionsToUse.setScope( lockOptions.getScope() ); - // some dialects wont allow locking with paging... - final RowSelection rowSelection = parameters.getRowSelection(); - final LimitHandler limitHandler = dialect.buildLimitHandler( sql, rowSelection ); - if ( LimitHelper.useLimit( limitHandler, rowSelection ) ) { - // user has requested a combination of paging and locking. See if the dialect supports that - // (ahem, Oracle...) - if ( ! dialect.supportsLockingAndPaging() ) { - LOG.delayedLockingDueToPaging(); - - // this one is kind of ugly. currently we do not track the needed alias-to-entity - // mapping into the "hydratedEntities" which drives these callbacks - // so for now apply the root lock mode to all. The root lock mode is listed in - // the alias specific map under the alias "this_"... - final LockOptions lockOptionsToUse = new LockOptions(); - lockOptionsToUse.setLockMode( lockOptions.getEffectiveLockMode( "this_" ) ); - lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); - lockOptionsToUse.setScope( lockOptions.getScope() ); - - afterLoadActions.add( - new AfterLoadAction() { - @Override - public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { - ( (Session) session ).buildLockRequest( lockOptionsToUse ) - .lock( persister.getEntityName(), entity ); - } + afterLoadActions.add( + new AfterLoadAction() { + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( lockOptionsToUse ) + .lock( persister.getEntityName(), entity ); } - ); - parameters.setLockOptions( new LockOptions() ); - return sql; - } + } + ); + parameters.setLockOptions( new LockOptions() ); + return sql; } - // there are other conditions we might want to add here, such as checking the result types etc - // but those are better served after we have redone the SQL generation to use ASTs. - final LockOptions locks = new LockOptions(lockOptions.getLockMode()); locks.setScope( lockOptions.getScope()); locks.setTimeOut( lockOptions.getTimeOut()); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java index a1c997390b..3d20381265 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java @@ -23,7 +23,6 @@ */ package org.hibernate.loader.hql; -import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -39,12 +38,8 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryException; import org.hibernate.ScrollableResults; -import org.hibernate.Session; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.QueryParameters; -import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventSource; @@ -334,7 +329,7 @@ public class QueryLoader extends BasicLoader { // user is request locking, lets see if we can apply locking directly to the SQL... // some dialects wont allow locking with paging... - if ( shouldDelayLockingDueToPaging( sql, parameters, dialect, afterLoadActions ) ) { + if ( shouldUseFollowOnLocking( parameters, dialect, afterLoadActions ) ) { return sql; } From 21ade0c798ed0fbb1444339af396325f324f94cb Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 30 Nov 2012 12:09:15 -0600 Subject: [PATCH 17/58] HHH-1168 - Problem combining locking and paging on Oracle --- .../main/java/org/hibernate/LockOptions.java | 61 ++++++++++++++----- .../java/org/hibernate/dialect/Dialect.java | 8 +++ .../hibernate/internal/CoreMessageLogger.java | 12 +++- .../java/org/hibernate/loader/Loader.java | 19 +++++- .../loader/criteria/CriteriaLoader.java | 18 +++++- 5 files changed, 98 insertions(+), 20 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/LockOptions.java b/hibernate-core/src/main/java/org/hibernate/LockOptions.java index a86b8b4f91..016a8ced22 100644 --- a/hibernate-core/src/main/java/org/hibernate/LockOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/LockOptions.java @@ -49,6 +49,7 @@ public class LockOptions implements Serializable { * UPGRADE represents LockMode.UPGRADE (will wait forever for lock and * scope of false meaning only entity is locked) */ + @SuppressWarnings("deprecation") public static final LockOptions UPGRADE = new LockOptions(LockMode.UPGRADE); /** @@ -65,7 +66,9 @@ public class LockOptions implements Serializable { private LockMode lockMode = LockMode.NONE; private int timeout = WAIT_FOREVER; - private Map aliasSpecificLockModes = null; //initialize lazily as LockOptions is frequently created without needing this + + //initialize lazily as LockOptions is frequently created without needing this + private Map aliasSpecificLockModes = null; public LockOptions() { } @@ -114,7 +117,7 @@ public class LockOptions implements Serializable { */ public LockOptions setAliasSpecificLockMode(String alias, LockMode lockMode) { if ( aliasSpecificLockModes == null ) { - aliasSpecificLockModes = new HashMap(); + aliasSpecificLockModes = new HashMap(); } aliasSpecificLockModes.put( alias, lockMode ); return this; @@ -135,7 +138,7 @@ public class LockOptions implements Serializable { if ( aliasSpecificLockModes == null ) { return null; } - return (LockMode) aliasSpecificLockModes.get( alias ); + return aliasSpecificLockModes.get( alias ); } /** @@ -159,6 +162,11 @@ public class LockOptions implements Serializable { return lockMode == null ? LockMode.NONE : lockMode; } + public boolean hasAliasSpecificLockModes() { + return aliasSpecificLockModes != null + && ! aliasSpecificLockModes.isEmpty(); + } + /** * Get the number of aliases that have specific lock modes defined. * @@ -183,6 +191,30 @@ public class LockOptions implements Serializable { return aliasSpecificLockModes.entrySet().iterator(); } + /** + * Currently needed for follow-on locking + * + * @return The greatest of all requested lock modes. + */ + public LockMode findGreatestLockMode() { + LockMode lockModeToUse = getLockMode(); + if ( lockModeToUse == null ) { + lockModeToUse = LockMode.NONE; + } + + if ( aliasSpecificLockModes == null ) { + return lockModeToUse; + } + + for ( LockMode lockMode : aliasSpecificLockModes.values() ) { + if ( lockMode.greaterThan( lockModeToUse ) ) { + lockModeToUse = lockMode; + } + } + + return lockModeToUse; + } + /** * Retrieve the current timeout setting. *

@@ -245,19 +277,20 @@ public class LockOptions implements Serializable { } /** - * Shallow copy From to Dest + * Perform a shallow copy * - * @param from is copied from - * @param dest is copied to - * @return dest + * @param source Source for the copy (copied from) + * @param destination Destination for the copy (copied to) + * + * @return destination */ - public static LockOptions copy(LockOptions from, LockOptions dest) { - dest.setLockMode(from.getLockMode()); - dest.setScope(from.getScope()); - dest.setTimeOut(from.getTimeOut()); - if ( from.aliasSpecificLockModes != null ) { - dest.aliasSpecificLockModes = new HashMap( from.aliasSpecificLockModes ); + public static LockOptions copy(LockOptions source, LockOptions destination) { + destination.setLockMode( source.getLockMode() ); + destination.setScope( source.getScope() ); + destination.setTimeOut( source.getTimeOut() ); + if ( source.aliasSpecificLockModes != null ) { + destination.aliasSpecificLockModes = new HashMap( source.aliasSpecificLockModes ); } - return dest; + return destination; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index ce2aa4653f..1b8cd205c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2400,6 +2400,14 @@ public abstract class Dialect implements ConversionContext { return false; } + /** + * Some dialects have trouble applying pessimistic locking depending upon what other query options are + * specified (paging, ordering, etc). This method allows these dialects to request that locking be applied + * by subsequent selects. + * + * @return {@code true} indicates that the dialect requests that locking be applied by subsequent select; + * {@code false} (the default) indicates that locking should be applied to the main SQL statement.. + */ public boolean useFollowOnLocking() { return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 20fce8b934..f4e10225c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -45,6 +45,7 @@ import org.jboss.logging.Message; import org.jboss.logging.MessageLogger; import org.hibernate.HibernateException; +import org.hibernate.LockMode; import org.hibernate.cache.CacheException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.loading.internal.CollectionLoadContext; @@ -1587,8 +1588,17 @@ public interface CoreMessageLogger extends BasicLogger { @LogMessage(level = WARN) @Message( value = "Encountered request for locking however dialect reports that database prefers locking be done in a " + - "separate select; results will be locked after initial query executes", + "separate select (follow-on locking); results will be locked after initial query executes", id = 444 ) void usingFollowOnLocking(); + + @LogMessage(level = WARN) + @Message( + value = "Alias-specific lock modes requested, which is not currently supported with follow-on locking; " + + "all acquired locks will be [%s]", + id = 445 + ) + void aliasSpecificLockingWithFollowOnLocking(LockMode lockMode); + } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 67d1907e49..66a618930d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -255,13 +255,16 @@ public abstract class Loader { List afterLoadActions) { if ( dialect.useFollowOnLocking() ) { LOG.usingFollowOnLocking(); - final LockOptions lockOptions = parameters.getLockOptions(); + // currently only one lock mode is allowed in follow-on locking + final LockMode lockMode = determineFollowOnLockMode( parameters.getLockOptions() ); + final LockOptions lockOptions = new LockOptions( lockMode ); + lockOptions.setTimeOut( parameters.getLockOptions().getTimeOut() ); + lockOptions.setScope( parameters.getLockOptions().getScope() ); afterLoadActions.add( new AfterLoadAction() { - private final LockOptions originalLockOptions = lockOptions.makeCopy(); @Override public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { - ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); + ( (Session) session ).buildLockRequest( lockOptions ).lock( persister.getEntityName(), entity ); } } ); @@ -271,6 +274,16 @@ public abstract class Loader { return false; } + protected LockMode determineFollowOnLockMode(LockOptions lockOptions) { + final LockMode lockModeToUse = lockOptions.findGreatestLockMode(); + + if ( lockOptions.hasAliasSpecificLockModes() ) { + LOG.aliasSpecificLockingWithFollowOnLocking( lockModeToUse ); + } + + return lockModeToUse; + } + private String prependComment(String sql, QueryParameters parameters) { String comment = parameters.getComment(); if ( comment == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index c5f0add9a6..521ffac762 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -207,8 +207,9 @@ public class CriteriaLoader extends OuterJoinLoader { if ( dialect.useFollowOnLocking() ) { // Dialect prefers to perform locking in a separate step LOG.usingFollowOnLocking(); - final LockOptions lockOptionsToUse = new LockOptions(); - lockOptionsToUse.setLockMode( lockOptions.getEffectiveLockMode( "this_" ) ); + + final LockMode lockMode = determineFollowOnLockMode( lockOptions ); + final LockOptions lockOptionsToUse = new LockOptions( lockMode ); lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); lockOptionsToUse.setScope( lockOptions.getScope() ); @@ -245,6 +246,19 @@ public class CriteriaLoader extends OuterJoinLoader { return dialect.applyLocksToSql( sql, locks, keyColumnNames ); } + + + protected LockMode determineFollowOnLockMode(LockOptions lockOptions) { + final LockMode lockModeToUse = lockOptions.findGreatestLockMode(); + + if ( lockOptions.getAliasLockCount() > 1 ) { + // > 1 here because criteria always uses alias map for the root lock mode (under 'this_') + LOG.aliasSpecificLockingWithFollowOnLocking( lockModeToUse ); + } + + return lockModeToUse; + } + protected LockMode[] getLockModes(LockOptions lockOptions) { final String[] entityAliases = getAliases(); if ( entityAliases == null ) { From 516577ea80b5b7ea72ead279bd804315f6faeb8d Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Wed, 28 Nov 2012 17:36:40 +0100 Subject: [PATCH 18/58] HHH-7827 - Correlating aggregated and outer queries --- .../criteria/AggregatedAuditExpression.java | 22 ++++++++++ .../envers/tools/query/QueryBuilder.java | 4 ++ .../query/MaximalizePropertyQuery.java | 41 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java index 1f4003e9d8..ab8b84086b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java @@ -32,10 +32,13 @@ import org.hibernate.envers.tools.query.QueryBuilder; /** * @author Adam Warski (adam at warski dot org) + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCriterion { private PropertyNameGetter propertyNameGetter; private AggregatedMode mode; + // Correlate subquery with outer query by entity id. + private boolean correlate = false; private List criterions; public AggregatedAuditExpression(PropertyNameGetter propertyNameGetter, AggregatedMode mode) { @@ -80,7 +83,26 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit subQb.addProjection("max", propertyName, false); } + // Correlating subquery with the outer query by entity id. See JIRA HHH-7827. + if ( correlate ) { + final String originalIdPropertyName = auditCfg.getAuditEntCfg().getOriginalIdPropName(); + auditCfg.getEntCfg().get( entityName ).getIdMapper().addIdsEqualToQuery( + subQb.getRootParameters(), + subQb.getRootAlias() + "." + originalIdPropertyName, + qb.getRootAlias() + "." + originalIdPropertyName + ); + } + // Adding the constrain on the result of the aggregated criteria subParams.addWhere(propertyName, "=", subQb); } + + /** + * Correlates aggregated subquery with the main query by entity id. + * @return this (for method chaining). + */ + public AggregatedAuditExpression correlateSubquery() { + correlate = true; + return this; + } } \ No newline at end of file diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/tools/query/QueryBuilder.java b/hibernate-envers/src/main/java/org/hibernate/envers/tools/query/QueryBuilder.java index 63ba0fb7d7..10cef5731b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/tools/query/QueryBuilder.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/tools/query/QueryBuilder.java @@ -182,6 +182,10 @@ public class QueryBuilder { return aliasList; } + public String getRootAlias() { + return alias; + } + private List getFromList() { List fromList = new ArrayList(); for (Pair from : froms) { diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java index 01c999273e..7efb33f301 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java @@ -30,6 +30,8 @@ import java.util.Set; import javax.persistence.EntityManager; +import org.hibernate.envers.RevisionType; +import org.hibernate.envers.enhanced.SequenceIdRevisionEntity; import org.hibernate.envers.query.AuditEntity; import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; @@ -42,12 +44,14 @@ import org.junit.Test; /** * @author Adam Warski (adam at warski dot org) + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ @SuppressWarnings({"unchecked"}) public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { Integer id1; Integer id2; Integer id3; + Integer id4; @Override protected Class[] getAnnotatedClasses() { @@ -64,14 +68,17 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { StrIntTestEntity site1 = new StrIntTestEntity("a", 10); StrIntTestEntity site2 = new StrIntTestEntity("b", 15); StrIntTestEntity site3 = new StrIntTestEntity("c", 42); + StrIntTestEntity site4 = new StrIntTestEntity("d", 52); em.persist(site1); em.persist(site2); em.persist(site3); + em.persist(site4); id1 = site1.getId(); id2 = site2.getId(); id3 = site3.getId(); + id4 = site4.getId(); em.getTransaction().commit(); @@ -107,6 +114,12 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { site2.setStr1("a"); em.getTransaction().commit(); + + // Revision 5 + em.getTransaction().begin(); + site4 = em.find( StrIntTestEntity.class, id4 ); + em.remove( site4 ); + em.getTransaction().commit(); } @Test @@ -170,4 +183,32 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { } } } + + @Test + @TestForIssue(jiraKey = "HHH-7827") + public void testAllLatestRevisionsOfEntityType() { + List result = getAuditReader().createQuery() + .forRevisionsOfEntity( StrIntTestEntity.class, false, true ) + .add( AuditEntity.revisionNumber().maximize().correlateSubquery() ) + .addOrder( AuditEntity.property( "id" ).asc() ) + .getResultList(); + + Assert.assertEquals( 4, result.size() ); + + Object[] result1 = (Object[]) result.get( 0 ); + Object[] result2 = (Object[]) result.get( 1 ); + Object[] result3 = (Object[]) result.get( 2 ); + Object[] result4 = (Object[]) result.get( 3 ); + + checkRevisionData( result1, 4, RevisionType.MOD, new StrIntTestEntity( "d", 5, id1 ) ); + checkRevisionData( result2, 4, RevisionType.MOD, new StrIntTestEntity( "a", 20, id2 ) ); + checkRevisionData( result3, 1, RevisionType.ADD, new StrIntTestEntity( "c", 42, id3 ) ); + checkRevisionData( result4, 5, RevisionType.DEL, new StrIntTestEntity( null, null, id4 ) ); + } + + private void checkRevisionData(Object[] result, int revision, RevisionType type, StrIntTestEntity entity) { + Assert.assertEquals( entity, result[0] ); + Assert.assertEquals( revision, ( (SequenceIdRevisionEntity) result[1] ).getId() ); + Assert.assertEquals( type, result[2] ); + } } \ No newline at end of file From da32690828d59c489241701c4107cc22033b519a Mon Sep 17 00:00:00 2001 From: Lukasz Antoniak Date: Wed, 5 Dec 2012 10:08:13 +0100 Subject: [PATCH 19/58] HHH-7827 - Documentation --- documentation/src/main/docbook/devguide/en-US/Envers.xml | 4 +++- .../envers/query/criteria/AggregatedAuditExpression.java | 9 +++++---- .../test/integration/query/MaximalizePropertyQuery.java | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/documentation/src/main/docbook/devguide/en-US/Envers.xml b/documentation/src/main/docbook/devguide/en-US/Envers.xml index 2f04dc4469..7f9f05cb37 100644 --- a/documentation/src/main/docbook/devguide/en-US/Envers.xml +++ b/documentation/src/main/docbook/devguide/en-US/Envers.xml @@ -898,7 +898,9 @@ query.add(AuditEntity.relatedId("address").eq(relatedEntityId));]]> The minimize() and maximize() methods return a criteria, to which you can add constraints, which must be met by the entities with the - maximized/minimized properties. + maximized/minimized properties. AggregatedAuditExpression#computeAggregationInInstanceContext() + enables the possibility to compute aggregated expression in the context of each entity instance + separately. It turns out useful when querying for latest revisions of all entities of a particular type. diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java index ab8b84086b..bf6518417c 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/criteria/AggregatedAuditExpression.java @@ -37,8 +37,7 @@ import org.hibernate.envers.tools.query.QueryBuilder; public class AggregatedAuditExpression implements AuditCriterion, ExtendableCriterion { private PropertyNameGetter propertyNameGetter; private AggregatedMode mode; - // Correlate subquery with outer query by entity id. - private boolean correlate = false; + private boolean correlate = false; // Correlate subquery with outer query by entity id. private List criterions; public AggregatedAuditExpression(PropertyNameGetter propertyNameGetter, AggregatedMode mode) { @@ -98,10 +97,12 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit } /** - * Correlates aggregated subquery with the main query by entity id. + * Compute aggregated expression in the context of each entity instance separately. Useful for retrieving latest + * revisions of all entities of a particular type.
+ * Implementation note: Correlates subquery with the outer query by entity id. * @return this (for method chaining). */ - public AggregatedAuditExpression correlateSubquery() { + public AggregatedAuditExpression computeAggregationInInstanceContext() { correlate = true; return this; } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java index 7efb33f301..764075cc45 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/MaximalizePropertyQuery.java @@ -189,7 +189,7 @@ public class MaximalizePropertyQuery extends BaseEnversJPAFunctionalTestCase { public void testAllLatestRevisionsOfEntityType() { List result = getAuditReader().createQuery() .forRevisionsOfEntity( StrIntTestEntity.class, false, true ) - .add( AuditEntity.revisionNumber().maximize().correlateSubquery() ) + .add( AuditEntity.revisionNumber().maximize().computeAggregationInInstanceContext() ) .addOrder( AuditEntity.property( "id" ).asc() ) .getResultList(); From 2fad160bd69df9ffa3b5b18272316a931692fdff Mon Sep 17 00:00:00 2001 From: brmeyer Date: Thu, 6 Dec 2012 14:26:13 -0500 Subject: [PATCH 20/58] HHH-7835 Inefficient implementation of JarVisitorFactory.getBytesFromInputStream --- .../packaging/internal/JarVisitorFactory.java | 29 ++++---- .../jpa/test/packaging/JarVisitorTest.java | 72 +++++++++++++++++++ .../jpa/test/packaging/PackagingTestCase.java | 13 ++++ .../hibernate/jpa/test/packaging/empty.txt | 0 4 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/empty.txt diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java index 9e62ea9c2a..267cd47d31 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java @@ -21,6 +21,7 @@ */ package org.hibernate.jpa.packaging.internal; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -28,13 +29,13 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; -import org.jboss.logging.Logger; - -import org.hibernate.jpa.internal.EntityManagerMessageLogger; import org.hibernate.internal.util.StringHelper; +import org.hibernate.jpa.internal.EntityManagerMessageLogger; +import org.jboss.logging.Logger; /** * @author Emmanuel Bernard + * @author Brett Meyer */ public class JarVisitorFactory { @@ -194,18 +195,16 @@ public class JarVisitorFactory { } } - public static byte[] getBytesFromInputStream(InputStream inputStream) throws IOException { - int size; - byte[] tmpByte = new byte[ 4096 ]; - byte[] entryBytes = new byte[0]; - for ( ; ; ) { - size = inputStream.read( tmpByte ); - if ( size == -1 ) break; - byte[] current = new byte[ entryBytes.length + size ]; - System.arraycopy( entryBytes, 0, current, 0, entryBytes.length ); - System.arraycopy( tmpByte, 0, current, entryBytes.length, size ); - entryBytes = current; + public static byte[] getBytesFromInputStream( + InputStream inputStream) throws IOException { + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int numBytes; + byte[] data = new byte[4096]; + while ( ( numBytes = inputStream.read( data, 0, data.length ) ) != -1 ) { + buffer.write( data, 0, numBytes ); } - return entryBytes; + buffer.flush(); + return buffer.toByteArray(); } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java index 6794708dd4..b1fb148cb1 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/JarVisitorTest.java @@ -27,9 +27,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; @@ -61,6 +65,7 @@ import org.junit.Test; /** * @author Emmanuel Bernard * @author Hardy Ferentschik + * @author Brett Meyer */ @RequiresDialect( H2Dialect.class ) // Nothing dialect-specific -- no need to run in matrix. @SuppressWarnings("unchecked") @@ -307,6 +312,73 @@ public class JarVisitorTest extends PackagingTestCase { // Entry entry = new Entry( Carpet.class.getName(), null ); // assertTrue( entries[1].contains( entry ) ); } + + @Test + @TestForIssue(jiraKey = "HHH-7835") + public void testGetBytesFromInputStream() { + try { + File file = buildLargeJar(); + + long start = System.currentTimeMillis(); + InputStream stream = new BufferedInputStream( + new FileInputStream( file ) ); + int oldLength = getBytesFromInputStream( stream ).length; + stream.close(); + long oldTime = System.currentTimeMillis() - start; + + start = System.currentTimeMillis(); + stream = new BufferedInputStream( new FileInputStream( file ) ); + int newLength = JarVisitorFactory.getBytesFromInputStream( + stream ).length; + stream.close(); + long newTime = System.currentTimeMillis() - start; + + assertEquals( oldLength, newLength ); + assertTrue( oldTime > newTime ); + } + catch ( Exception e ) { + fail( e.getMessage() ); + } + } + + // This is the old getBytesFromInputStream from JarVisitorFactory before + // it was changed by HHH-7835. Use it as a regression test. + private byte[] getBytesFromInputStream( + InputStream inputStream) throws IOException { + int size; + + byte[] entryBytes = new byte[0]; + for ( ;; ) { + byte[] tmpByte = new byte[4096]; + size = inputStream.read( tmpByte ); + if ( size == -1 ) + break; + byte[] current = new byte[entryBytes.length + size]; + System.arraycopy( entryBytes, 0, current, 0, entryBytes.length ); + System.arraycopy( tmpByte, 0, current, entryBytes.length, size ); + entryBytes = current; + } + return entryBytes; + } + + @Test + @TestForIssue(jiraKey = "HHH-7835") + public void testGetBytesFromZeroInputStream() { + try { + // Ensure that JarVisitorFactory#getBytesFromInputStream + // can handle 0 length streams gracefully. + InputStream emptyStream = new BufferedInputStream( + new FileInputStream( new File( + "src/test/resources/org/hibernate/jpa/test/packaging/empty.txt" ) ) ); + int length = JarVisitorFactory.getBytesFromInputStream( + emptyStream ).length; + assertEquals( length, 0 ); + emptyStream.close(); + } + catch ( Exception e ) { + fail( e.getMessage() ); + } + } private Filter[] getFilters() { return new Filter[] { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java index 12a82dcd76..00d38de02b 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java @@ -73,6 +73,7 @@ import static org.junit.Assert.fail; /** * @author Hardy Ferentschik + * @author Brett Meyer */ public abstract class PackagingTestCase extends BaseCoreFunctionalTestCase { protected static ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); @@ -335,6 +336,18 @@ public abstract class PackagingTestCase extends BaseCoreFunctionalTestCase { return testPackage; } + protected File buildLargeJar() { + String fileName = "large.jar"; + JavaArchive archive = ShrinkWrap.create( JavaArchive.class, fileName ); + // Build a large jar by adding all EntityManager packages and + // subpackages on the classpath. + archive.addPackages(true, "org.hibernate.ejb", "org.hibernate.jpa" ); + + File testPackage = new File( packageTargetDir, fileName ); + archive.as( ZipExporter.class ).exportTo( testPackage, true ); + return testPackage; + } + protected File buildWar() { String fileName = "war.war"; WebArchive archive = ShrinkWrap.create( WebArchive.class, fileName ); diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/empty.txt b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/empty.txt new file mode 100644 index 0000000000..e69de29bb2 From 230dc55d80bbef019736cea338ca1febbfdb8b21 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Thu, 6 Dec 2012 14:58:10 -0500 Subject: [PATCH 21/58] HHH-7835 Inefficient implementation of JarVisitorFactory.getBytesFromInputStream --- .../packaging/internal/JarVisitorFactory.java | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java index 267cd47d31..45ca7f8e1b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/packaging/internal/JarVisitorFactory.java @@ -21,13 +21,14 @@ */ package org.hibernate.jpa.packaging.internal; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import java.util.LinkedList; +import java.util.List; import org.hibernate.internal.util.StringHelper; import org.hibernate.jpa.internal.EntityManagerMessageLogger; @@ -195,16 +196,37 @@ public class JarVisitorFactory { } } - public static byte[] getBytesFromInputStream( - InputStream inputStream) throws IOException { - - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int numBytes; - byte[] data = new byte[4096]; - while ( ( numBytes = inputStream.read( data, 0, data.length ) ) != -1 ) { - buffer.write( data, 0, numBytes ); + // Optimized by HHH-7835 + public static byte[] getBytesFromInputStream(InputStream inputStream) throws IOException { + int size; + List data = new LinkedList(); + int bufferSize = 4096; + byte[] tmpByte = new byte[bufferSize]; + int offset = 0; + int total = 0; + for ( ;; ) { + size = inputStream.read( tmpByte, offset, bufferSize - offset ); + if ( size == -1 ) + break; + + offset += size; + + if ( offset == tmpByte.length ) { + data.add( tmpByte ); + tmpByte = new byte[bufferSize]; + offset = 0; + total += tmpByte.length; + } } - buffer.flush(); - return buffer.toByteArray(); + + byte[] result = new byte[total + offset]; + int count = 0; + for ( byte[] arr : data ) { + System.arraycopy( arr, 0, result, count * arr.length, arr.length ); + count++; + } + System.arraycopy( tmpByte, 0, result, count * tmpByte.length, offset ); + + return result; } } From 1f690e7aa6a5f4511084aac9526fe3e6812f2632 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Thu, 6 Dec 2012 16:17:32 -0500 Subject: [PATCH 22/58] HHH-7835 Updated the largeJar test to be completely isolated --- .../jpa/test/packaging/PackagingTestCase.java | 9 +- .../jpa/test/packaging/loremipsum.txt | 201 ++++++++++++++++++ 2 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/loremipsum.txt diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java index 00d38de02b..30582808f6 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagingTestCase.java @@ -339,9 +339,12 @@ public abstract class PackagingTestCase extends BaseCoreFunctionalTestCase { protected File buildLargeJar() { String fileName = "large.jar"; JavaArchive archive = ShrinkWrap.create( JavaArchive.class, fileName ); - // Build a large jar by adding all EntityManager packages and - // subpackages on the classpath. - archive.addPackages(true, "org.hibernate.ejb", "org.hibernate.jpa" ); + // Build a large jar by adding a lorem ipsum file repeatedly. + for ( int i = 0; i < 100; i++ ) { + ArchivePath path = ArchivePaths.create( "META-INF/file" + i ); + archive.addAsResource( new File( "src/test/resources/org/hibernate/jpa/test/packaging/loremipsum.txt" ), + path ); + } File testPackage = new File( packageTargetDir, fileName ); archive.as( ZipExporter.class ).exportTo( testPackage, true ); diff --git a/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/loremipsum.txt b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/loremipsum.txt new file mode 100644 index 0000000000..5d2f1703e3 --- /dev/null +++ b/hibernate-entitymanager/src/test/resources/org/hibernate/jpa/test/packaging/loremipsum.txt @@ -0,0 +1,201 @@ + + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi massa dui, venenatis ac semper sed, pellentesque eget mi. Maecenas a neque purus, sed vehicula justo. Donec sit amet elit eget nunc porta aliquet vel et libero. Cras ut sem sem. In porta, quam sit amet fringilla molestie, mi turpis molestie dui, vulputate ullamcorper sem nunc in justo. Sed tempor lectus ac justo lobortis a dictum odio malesuada. Praesent a odio sapien. Donec ut nisl mi, ut molestie leo. In at odio nisl, vel luctus elit. Quisque vel odio ut turpis adipiscing venenatis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; + +Aenean sagittis, ligula vitae semper gravida, elit justo pulvinar magna, in condimentum ipsum magna non diam. Quisque sodales, quam sed convallis scelerisque, erat metus ultricies diam, a varius nunc lectus id dolor. Duis sed magna accumsan augue ornare egestas a quis arcu. Quisque tempor ligula id ipsum mollis dapibus. Fusce vitae mi sapien. Aliquam tincidunt est quis enim lobortis sed pharetra tortor consequat. Sed congue est at tortor suscipit pellentesque. Aliquam libero purus, mattis vitae porttitor ac, ullamcorper in tellus. + +Etiam vitae elit sit amet dolor aliquet volutpat. Pellentesque condimentum quam non mauris mollis rhoncus. Fusce sed ante nisl, et elementum sapien. Morbi blandit est vitae mi viverra malesuada. Integer at pharetra neque. Aliquam vitae diam tellus. Nullam tincidunt laoreet placerat. Sed commodo tempor mauris, ac imperdiet mi elementum at. Aliquam erat volutpat. Etiam sem neque, dictum quis faucibus nec, convallis ut nisi. Ut a justo nulla, ac fringilla ante. Suspendisse pellentesque varius tincidunt. Vivamus non leo eu sem euismod aliquet at ut urna. Mauris auctor cursus mi, vel placerat tortor rhoncus sed. Quisque varius tortor in quam commodo in dictum tortor aliquam. + +Nullam adipiscing turpis in mauris consequat eu fermentum leo varius. Donec sed elit sed sem lacinia tincidunt. Nam eleifend velit eu neque egestas luctus. Suspendisse pellentesque ultrices consectetur. Integer blandit facilisis mattis. Nunc a accumsan nisi. Phasellus urna eros, pretium eget fringilla vel, tincidunt quis leo. Morbi sem arcu, fringilla facilisis posuere sed, rhoncus et felis. Duis in tortor sed justo tempus elementum. Pellentesque tincidunt dolor aliquet tortor accumsan condimentum. Nunc volutpat, lacus a rhoncus bibendum, dolor augue consequat lorem, cursus venenatis felis ante vitae turpis. Maecenas nec nunc sit amet nunc tempor laoreet. Praesent semper mi tincidunt quam suscipit vitae congue diam bibendum. + +Proin eget leo vitae erat consectetur vulputate quis eu quam. Sed nulla velit, dignissim eu viverra quis, mattis cursus est. Vestibulum consequat, tortor ut consectetur venenatis, ipsum neque pharetra mi, et fringilla velit purus pellentesque erat. Nunc congue varius condimentum. Aliquam ornare mattis dignissim. Fusce leo nulla, tempus et sagittis vitae, sodales eget leo. Morbi lacinia, justo interdum scelerisque tristique, ante mi pulvinar mauris, at hendrerit nibh nunc ut mauris. Curabitur sit amet diam nec libero vestibulum iaculis in ac nunc. Quisque dictum sapien malesuada quam mattis facilisis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla sed velit a ipsum hendrerit pulvinar. Nam dolor ipsum, iaculis imperdiet imperdiet id, condimentum quis sapien. Vivamus nec mauris metus, non rhoncus odio. Fusce ut hendrerit nisi. + +Phasellus ipsum velit, suscipit nec fringilla et, commodo in velit. Fusce eu odio mi. Curabitur a gravida libero. Vestibulum egestas rutrum ligula, eget suscipit est consequat non. Pellentesque sollicitudin pretium ante, vitae auctor velit vulputate ac. Cras sit amet purus quis est vestibulum sollicitudin. Donec faucibus nunc id velit pulvinar quis ultricies lacus accumsan. Ut vel ante risus. Morbi vel sem tortor. + +Pellentesque vestibulum vestibulum urna et lacinia. Proin interdum gravida pulvinar. Aliquam erat volutpat. Vivamus vel odio lacus. Nullam quis odio nec libero mattis fermentum in non arcu. In egestas, diam vitae porta porttitor, nisi mi vulputate neque, ac tristique nisl massa vel enim. Aenean tortor nunc, accumsan ut luctus volutpat, congue non tellus. + +Donec laoreet sem eu neque tincidunt viverra. Nullam nec velit at ante suscipit auctor sit amet non augue. Mauris dignissim tortor et nunc venenatis faucibus tempus purus mollis. Morbi suscipit iaculis elit ac faucibus. Pellentesque eget massa orci. Phasellus at est odio. Ut tempor justo et orci porttitor lacinia. Phasellus neque dolor, condimentum at fermentum tincidunt, porta vel eros. Maecenas eu felis nec orci rutrum porta. + +Proin sollicitudin cursus bibendum. Praesent vehicula augue id arcu feugiat at semper mi cursus. Cras sodales nulla at lectus consequat feugiat. Aliquam ultrices, massa eget pretium adipiscing, erat ligula ornare nulla, sed convallis purus nunc sed libero. Aliquam enim ante, ultrices sit amet aliquam at, fringilla non nibh. Suspendisse tincidunt, massa ac facilisis porta, quam lacus aliquam neque, ut volutpat velit neque sit amet libero. Mauris nec purus eget ipsum suscipit ullamcorper a vitae mauris. Integer vitae mattis ligula. Etiam dictum dui ut eros gravida nec mollis sem elementum. In auctor varius mauris, vitae ullamcorper erat convallis et. Proin tincidunt, ligula sed ullamcorper imperdiet, urna mauris tincidunt felis, ac posuere est nisi et leo. Aenean a justo id massa egestas luctus eget eget orci. Pellentesque viverra, enim in laoreet fermentum, leo lorem fermentum nibh, ac tempor nisi enim ac ligula. + +Vivamus a enim eu justo euismod convallis eget eu mi. Vestibulum non lectus tristique quam aliquet blandit et eu nibh. In orci sapien, venenatis vel malesuada vitae, tempor in magna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi vel nisi nulla. Aenean pharetra feugiat congue. Quisque auctor, odio sit amet venenatis lobortis, tortor tortor hendrerit mauris, et elementum odio lorem a arcu. Morbi commodo consectetur augue quis vestibulum. Cras dictum orci et lorem cursus aliquet. In urna metus, tristique cursus convallis sit amet, condimentum sed est. Morbi dapibus ullamcorper ipsum, id scelerisque nunc scelerisque id. Ut vel justo neque, sit amet venenatis arcu. Praesent faucibus facilisis tellus, nec placerat enim viverra quis. Suspendisse potenti. Vivamus placerat sagittis sapien, ac ultricies diam fermentum at. Duis enim elit, fringilla a venenatis a, feugiat at augue. + +Curabitur tortor tortor, rutrum sit amet fringilla vel, dignissim nec ligula. Nam ipsum eros, consequat eu consectetur id, sollicitudin vel dolor. Cras fringilla ornare nulla quis dictum. Nunc porttitor rutrum purus ut tempor. Sed aliquet, mauris at ultricies viverra, nisl ipsum aliquet ligula, eu pharetra orci est ac ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur imperdiet luctus ultricies. Quisque rutrum scelerisque consequat. Morbi semper aliquet sapien. Sed a purus eget risus volutpat viverra nec sit amet diam. Sed dapibus, nisi in commodo tempus, libero risus feugiat diam, ac sagittis tellus odio non metus. Donec quis augue et neque laoreet suscipit sed ut dui. Mauris massa ligula, lobortis id egestas accumsan, adipiscing mattis mauris. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut eleifend est semper augue elementum tempor. + +Phasellus eget lorem quis neque iaculis adipiscing sed non orci. Vivamus tincidunt auctor malesuada. Pellentesque vitae ante nisi, iaculis egestas ligula. Maecenas pellentesque orci sed arcu euismod vehicula. Nullam rutrum hendrerit est eu dapibus. Morbi scelerisque sem a turpis interdum venenatis. Proin congue sapien enim, ac convallis sem. Curabitur ornare accumsan ullamcorper. Curabitur convallis nulla at est dignissim ut scelerisque nulla ultrices. Mauris mauris urna, auctor at mattis vel, venenatis at diam. Donec fermentum varius magna, vitae suscipit velit suscipit vel. Nullam justo eros, sollicitudin a malesuada vel, ultrices nec justo. Aliquam sapien turpis, facilisis vel venenatis id, vulputate euismod tellus. Sed metus neque, facilisis quis cursus quis, convallis id elit. + +Nullam a dolor erat. Vivamus id mauris in libero hendrerit mollis eget eget massa. Quisque gravida massa ut erat molestie sed pulvinar eros feugiat. Aenean id purus et purus ultricies condimentum id aliquam enim. Vivamus rhoncus nibh nec dolor blandit eleifend. Curabitur sollicitudin urna vitae mi blandit vehicula. Nunc nec leo sed nisl lobortis condimentum. Nunc ultricies ullamcorper aliquet. In sit amet erat dolor, sit amet luctus orci. Sed placerat, urna ut faucibus convallis, lorem nisl placerat nisi, eget viverra mauris arcu nec dui. Duis dictum, risus sit amet aliquet eleifend, nibh elit laoreet nulla, ac gravida justo nisl vel sem. Sed ac lacus lacus. In venenatis malesuada massa non porta. Sed tortor nibh, volutpat ut hendrerit vel, facilisis eget velit. + +Sed quis erat vel massa blandit convallis at et nibh. Vivamus viverra lorem ac metus condimentum auctor. In volutpat massa nec elit sollicitudin porttitor. Praesent eu est eros. Nulla cursus risus id tellus malesuada tincidunt. Nam mattis, mi at ornare fermentum, lorem nunc eleifend lacus, ut lobortis est elit et justo. Aliquam egestas venenatis tempus. Donec a augue vel nisl sollicitudin gravida eget eu ligula. Sed id turpis nisi. Maecenas vel augue libero, sed facilisis risus. Vivamus a augue in justo feugiat auctor. Maecenas vel nisi vitae lectus lacinia tincidunt. Nam suscipit erat id elit tincidunt condimentum tincidunt est aliquam. Proin at mauris sed felis interdum aliquet sed id neque. + +Nullam nisi dui, aliquam sed venenatis quis, rhoncus non orci. Integer congue pulvinar mi, id vestibulum dolor bibendum eu. Nulla eu ligula sit amet orci aliquet bibendum eu quis augue. Suspendisse potenti. Quisque hendrerit vestibulum risus ut sodales. Donec enim orci, cursus eget sodales id, mattis non tortor. Sed in velit dolor, vel pretium ligula. Ut vel auctor lacus. Duis vitae odio nec augue vestibulum bibendum at in eros. Phasellus nec dolor mauris. Quisque a felis tortor, non sollicitudin enim. Fusce ut erat pellentesque mauris malesuada bibendum quis ut lorem. Proin ut ante sit amet eros laoreet auctor in eget erat. Fusce neque tortor, eleifend in consequat vitae, scelerisque a libero. Sed eget euismod neque. + +Pellentesque rutrum, orci a scelerisque porta, urna dolor rutrum eros, ac varius mauris nulla eget lorem. Etiam suscipit faucibus ipsum vitae facilisis. Integer varius ultrices nisi in porttitor. Nulla facilisi. Nam vitae accumsan urna. Aliquam vehicula, dui nec molestie lacinia, tellus sem malesuada tellus, molestie interdum magna nibh non enim. Cras ultrices, elit nec vestibulum facilisis, massa dolor luctus tortor, ut consectetur erat arcu tincidunt lorem. + +Integer et nibh vel mi vulputate scelerisque vitae ac velit. Suspendisse potenti. Aliquam vestibulum facilisis felis, et semper massa rutrum et. Cras ac enim metus. Ut ultricies, mauris ac mattis hendrerit, augue metus accumsan purus, in gravida velit lectus ut sapien. Proin tristique justo et velit vestibulum vel adipiscing lorem sodales. Fusce ultricies diam sit amet dolor imperdiet sit amet vestibulum augue dictum. Integer sit amet tortor purus. Aenean sagittis eros convallis urna iaculis porttitor. Duis posuere pharetra purus eu tristique. Aenean nec dolor leo, sit amet lobortis purus. Donec tempus molestie convallis. In hac habitasse platea dictumst. + +Donec sollicitudin, nisi sit amet scelerisque malesuada, ipsum tortor ullamcorper mi, porta bibendum quam augue a lorem. Nulla fermentum venenatis turpis non pretium. Vestibulum eleifend, nisi facilisis placerat ultricies, justo massa ultricies turpis, quis dapibus nunc arcu a lorem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In hac habitasse platea dictumst. Phasellus eleifend, odio ac facilisis facilisis, nulla magna porttitor metus, id scelerisque augue magna in eros. Curabitur consequat egestas justo eget iaculis. Nulla consequat, nisi aliquet mattis faucibus, dolor lectus malesuada tortor, sit amet mollis eros sem non lorem. + +Vestibulum vitae velit lectus, a luctus massa. Donec et diam purus. Aliquam erat volutpat. Fusce ut auctor quam. Phasellus iaculis, nulla sit amet pretium blandit, est turpis ultricies nunc, sit amet tempor nisi tortor luctus nibh. Nam sit amet justo leo. In pellentesque, magna sed lacinia faucibus, ipsum ipsum adipiscing ipsum, sit amet ultrices lorem massa ut massa. + +Donec pulvinar lacus at metus aliquam rutrum. Pellentesque quis dignissim tortor. Maecenas ut ultricies urna. Mauris rhoncus molestie mollis. Phasellus sagittis velit sed risus tempus scelerisque. Ut id purus eget nunc placerat sodales. Praesent at vehicula enim. Vivamus molestie tincidunt dolor, id tristique est vulputate non. Proin feugiat diam sed elit tincidunt nec eleifend eros malesuada. Duis tristique leo a neque vestibulum vel mattis libero dignissim. Vestibulum placerat tristique ante eu volutpat. Etiam auctor interdum mauris, a consequat libero ullamcorper a. Mauris dignissim metus sit amet lectus vehicula quis sodales diam fermentum. Nullam non lobortis tortor. + +Morbi mattis magna sed felis suscipit a porta massa sollicitudin. Integer ut magna diam, vitae posuere est. Nunc rhoncus dui et sapien dapibus a congue lectus auctor. Etiam ullamcorper enim in purus faucibus sollicitudin. Pellentesque sit amet velit et mi fermentum iaculis vel et neque. Mauris lacinia feugiat est et sollicitudin. In laoreet, magna in malesuada interdum, nisl tellus placerat massa, non dignissim nunc nisl vitae mauris. Maecenas vitae varius elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur lorem eros, sollicitudin et accumsan ac, rutrum ut nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent elit ipsum, imperdiet a lobortis nec, pulvinar vel magna. + +Proin dapibus porta nisi eget ultrices. Nullam sed nisl eu odio scelerisque dignissim ut nec nibh. Nulla vitae risus dolor, interdum semper nisi. Pellentesque porttitor turpis vitae est scelerisque bibendum. Integer malesuada blandit adipiscing. Suspendisse potenti. Suspendisse at pretium velit. Ut felis nibh, pulvinar id sollicitudin non, dictum non dui. Aliquam bibendum orci vitae dui scelerisque bibendum. Etiam blandit diam eu nunc congue tristique. Proin sit amet rhoncus ante. Aliquam et magna in tellus condimentum tempor ac in orci. Sed facilisis facilisis nisl vel tincidunt. Etiam mauris augue, tincidunt at pretium nec, aliquam sed massa. + +Etiam libero ligula, feugiat nec blandit in, eleifend et lacus. Nulla commodo eleifend consectetur. Cras convallis, nisl vitae volutpat elementum, neque diam pharetra orci, at varius eros justo a augue. Aenean vitae bibendum erat. Aliquam ligula tortor, aliquet a pharetra non, aliquam eu quam. Aliquam molestie lacus vel augue laoreet non porta leo faucibus. Morbi placerat, risus vel viverra luctus, metus tellus adipiscing mauris, et malesuada elit est ut tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla nec ultrices erat. Etiam nec sapien eros, et eleifend mauris. Nam sem ante, elementum quis scelerisque vel, elementum sed est. Phasellus scelerisque, dolor elementum consectetur luctus, massa est commodo velit, posuere fermentum sem nisl vel augue. + +Aenean vel tortor ante. Quisque id turpis id ligula dignissim dictum ac eget lectus. Cras consectetur elit id diam vestibulum feugiat. Aliquam accumsan rhoncus orci, a elementum libero egestas sed. Morbi fringilla arcu non justo venenatis id elementum dolor adipiscing. Morbi porttitor, ligula eget sodales ultricies, urna neque lobortis ligula, non convallis turpis risus et sem. Maecenas feugiat lectus in mauris feugiat imperdiet. Ut justo urna, faucibus ut fermentum eu, sagittis ac nunc. Nullam at lacus at lacus molestie pharetra. Donec nunc dolor, ullamcorper sed molestie nec, tristique at neque. Vestibulum sagittis pharetra lorem, et tempus turpis accumsan nec. Morbi velit velit, auctor sed fermentum sed, pharetra a felis. Pellentesque lacus eros, molestie id mattis eu, tempus vitae felis. Mauris ac nisi a neque sagittis congue. Nullam id lectus ut tortor malesuada mollis imperdiet ut dolor. + +Duis ac nunc velit, tincidunt elementum nisi. Aenean volutpat venenatis justo, malesuada rhoncus sapien cursus egestas. Morbi consectetur adipiscing nulla, eu adipiscing nibh feugiat eget. Vestibulum lacinia dignissim felis, et fermentum arcu pulvinar ac. Sed nisl nulla, fringilla id tristique vitae, elementum porttitor ante. Nunc in metus magna. Etiam gravida rutrum lectus, sit amet dignissim orci condimentum vitae. Nulla eget sem nec est ultricies luctus. Maecenas porttitor cursus lobortis. Integer imperdiet iaculis lectus, eu rutrum risus viverra quis. Nullam volutpat elementum hendrerit. Vestibulum accumsan hendrerit rutrum. + +Nullam blandit pellentesque magna, id iaculis metus ultricies nec. Maecenas ipsum diam, vestibulum a gravida nec, sollicitudin vel arcu. Pellentesque nec odio in risus vulputate iaculis nec sed nunc. Quisque facilisis consequat nulla et tempus. Morbi suscipit ligula nec augue faucibus accumsan. Donec id mi arcu, eget viverra lectus. Curabitur in tellus augue, nec tristique mi. Cras elit leo, iaculis vel aliquet ac, adipiscing elementum dui. Fusce est nisl, luctus et ornare non, posuere at dui. Vivamus ligula mi, lobortis non suscipit vel, vulputate eget diam. Vestibulum tincidunt pulvinar leo, sed pellentesque leo sagittis sit amet. Nam luctus fermentum porttitor. Donec eu fringilla tellus. + +Nullam velit justo, faucibus ac accumsan quis, porta ut leo. Fusce dui nunc, hendrerit eu porta sit amet, ultricies et metus. Vivamus nisl eros, tempus lacinia volutpat et, vestibulum id lorem. In vitae quam et ligula ultricies ultrices. Proin varius sagittis metus non vehicula. Integer vulputate urna quis mauris eleifend sed vulputate ipsum dapibus. Nam varius, eros at tincidunt auctor, lacus neque feugiat sem, pulvinar pharetra velit lacus sed nisi. Cras nunc urna, pellentesque ac posuere ac, feugiat molestie dolor. Quisque fermentum mi ut orci consequat posuere. In dui tortor, convallis nec sagittis quis, pretium quis leo. Nullam et sapien justo, id ultrices orci. + +Mauris molestie, sapien nec laoreet fringilla, metus quam viverra nibh, quis venenatis felis sem eget sem. In at quam id libero viverra luctus. Aenean sit amet sem turpis, sit amet tincidunt tortor. Nam bibendum, ante vel vestibulum sodales, erat elit tempor mauris, ac rutrum neque elit eu nulla. Etiam aliquam, nisi lacinia cursus imperdiet, mi tellus gravida odio, eget vestibulum leo lectus et lectus. Mauris commodo quam eget odio ultricies imperdiet. Pellentesque fermentum risus quis enim tincidunt fringilla. Sed nulla massa, suscipit commodo lobortis eu, luctus sit amet lectus. Sed tellus metus, interdum mattis aliquet id, fringilla id dolor. Nam fermentum vulputate magna eget feugiat. Praesent mattis justo at mauris tempus dapibus. Nunc et risus eu felis molestie vulputate non eleifend lectus. + +Pellentesque facilisis cursus justo, eu auctor nibh vestibulum sit amet. Proin vitae urna eget felis cursus luctus. Morbi justo enim, dictum ac lacinia non, blandit at eros. Donec mauris mauris, iaculis nec feugiat eu, porta eget sapien. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla eu imperdiet elit. Aenean congue imperdiet est vel tempus. Integer ipsum velit, sagittis nec feugiat a, rhoncus eget ante. Proin vehicula mattis dui non varius. Ut in risus non nunc lobortis tempor gravida eget velit. + +Duis lobortis, massa a tincidunt sodales, quam dui sodales felis, id pulvinar velit augue ut massa. Mauris auctor augue consectetur sem euismod vestibulum. Curabitur vitae lacus sed quam vehicula iaculis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec in lectus felis, et dapibus magna. Nam pulvinar lectus vel lacus vehicula ornare pretium neque pretium. Duis dignissim dolor in libero egestas ultricies. Nullam vestibulum euismod tortor vitae cursus. Vestibulum volutpat, nulla id pharetra tempor, nisi nunc lobortis massa, at sollicitudin lorem lectus eget tellus. Aenean dictum ipsum eget dolor molestie ullamcorper. Sed nec nisi ut velit elementum convallis eget ut enim. Quisque tempus quam vitae nisi faucibus gravida. Etiam et nunc libero. + +Fusce varius nunc eget risus blandit et fringilla massa pulvinar. Duis odio felis, tempor commodo placerat non, fringilla a est. Phasellus velit nulla, vehicula non mollis semper, sodales non turpis. Cras eu erat dui, vitae feugiat magna. Donec et augue nulla. Sed ac velit ac eros interdum commodo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Nam pellentesque adipiscing dolor consectetur ornare. Donec libero risus, laoreet nec suscipit et, elementum sit amet risus. Praesent vitae velit at augue pellentesque posuere. Quisque eget massa libero. Pellentesque libero elit, consectetur et faucibus eu, pretium volutpat metus. Quisque leo diam, pulvinar ac eleifend sit amet, porta ac lorem. Integer non diam id lacus luctus tristique. Praesent ipsum enim, auctor vitae laoreet et, volutpat quis arcu. Nunc in augue lorem. Aenean vitae purus risus. Aenean vulputate odio sed enim fringilla eu euismod ipsum pulvinar. Nulla facilisi. Etiam leo diam, tristique ut ultrices id, feugiat semper nisl. Sed ligula justo, aliquet non suscipit non, porta elementum tortor. Quisque sed tortor nisl, nec molestie libero. + +Sed pellentesque, augue nec dictum fermentum, erat dui aliquet lorem, at luctus sapien lorem sit amet eros. Sed molestie, tortor vel varius congue, libero nulla semper velit, a feugiat eros urna non urna. Quisque vestibulum fringilla lectus in lacinia. Fusce eu augue nibh, ut dapibus ligula. Suspendisse potenti. Cras porttitor risus eget justo vulputate faucibus. Phasellus sed imperdiet felis. Aenean varius, libero a vestibulum varius, velit est volutpat erat, ut accumsan enim mauris vel odio. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Pellentesque lacinia, eros quis luctus molestie, felis nulla consequat metus, eu tincidunt elit ante et sem. Curabitur accumsan rutrum elit, id pellentesque odio sagittis ac. + +Donec rutrum sem et ligula ornare vestibulum. Ut ultrices mi vel libero placerat mattis. Phasellus pretium purus a est tempor iaculis pretium risus rutrum. Sed porttitor urna bibendum dui dapibus interdum. Ut sit amet nisi at est iaculis euismod nec placerat sem. Phasellus congue pulvinar semper. Cras gravida imperdiet erat, vitae venenatis sem vestibulum quis. Nulla a lectus ut turpis aliquam commodo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce blandit velit eu lectus fermentum aliquam. Phasellus feugiat, est vel tincidunt tristique, erat libero viverra mauris, vitae mollis felis massa sed velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce leo diam, feugiat vel mattis id, tincidunt ac enim. Ut justo dui, mollis mollis vehicula eu, luctus porta purus. + +Sed in odio sed nunc ultrices blandit eget ut enim. Praesent blandit, tortor vitae cursus ultrices, magna nunc congue arcu, luctus aliquet ligula est vestibulum ipsum. Sed pulvinar varius nulla, sit amet consequat erat vehicula volutpat. Ut sit amet diam a lorem posuere mattis. Maecenas vel convallis dui. Quisque ipsum tellus, condimentum eu cursus non, auctor eu turpis. Phasellus dictum, nibh non facilisis condimentum, nisl mi bibendum neque, eget elementum turpis diam vitae tortor. Suspendisse potenti. Ut varius magna in tortor laoreet ut posuere mauris fermentum. + +Sed interdum mauris quis nibh eleifend elementum. Nulla vel pharetra tortor. Suspendisse ornare imperdiet mi, vitae consectetur tortor pulvinar at. Sed orci est, semper non ultricies semper, lacinia vitae justo. Pellentesque eu diam ligula. Ut ut risus est. Praesent augue neque, vestibulum rhoncus aliquet et, facilisis vitae turpis. Nullam et nibh nisl, et consequat neque. Fusce justo tortor, tempus quis cursus non, varius id nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed facilisis interdum diam ut accumsan. Proin orci diam, adipiscing vel fringilla quis, elementum ut erat. Suspendisse nisi felis, facilisis congue mattis eget, blandit sit amet mi. Fusce et dui eros. + +Curabitur lobortis ligula non purus porta in tincidunt nisi imperdiet. Donec tincidunt orci quis augue volutpat at malesuada diam porttitor. Nulla facilisi. Integer non neque eu nisl dictum elementum id vel mauris. Aenean bibendum placerat viverra. Vivamus vulputate convallis nibh sed fringilla. Donec consequat, justo a auctor semper, nisl risus condimentum turpis, varius ultricies lorem neque congue nunc. Nulla volutpat orci et risus pretium ut ullamcorper nisi varius. Integer id turpis eget enim luctus placerat. Nulla massa libero, tincidunt in sagittis ut, feugiat vel metus. Aliquam vel congue massa. Vestibulum convallis nisi in arcu sodales vehicula. Phasellus quis laoreet dolor. + +Morbi eu blandit enim. Nunc sit amet nisi nec erat tempus scelerisque a nec felis. Curabitur semper mauris quis libero mattis tempus. Etiam congue tortor sit amet odio egestas viverra. Donec nec risus velit. Mauris dictum velit ut erat gravida iaculis. Cras posuere pharetra egestas. Vestibulum eget adipiscing tellus. Suspendisse mattis tellus at velit commodo et faucibus sem tincidunt. In aliquam sollicitudin arcu ac sagittis. Donec sit amet velit nunc. Fusce in lacus quis turpis vulputate rutrum. + +Donec ut ligula tincidunt enim commodo blandit. Maecenas iaculis lorem sit amet lacus feugiat eget gravida augue porttitor. Etiam aliquet lobortis dolor at gravida. Etiam id ante sit amet massa mollis vehicula. Integer vel felis eu metus luctus consequat id et justo. Nunc eu mi nisi. Sed laoreet risus sit amet augue vehicula rhoncus imperdiet eros suscipit. Nam auctor quam sit amet dolor tempor tempor. Donec condimentum, diam tempor semper eleifend, est mi malesuada orci, id convallis diam sem eu tellus. Aenean lacinia bibendum ornare. Suspendisse eu lacus vitae augue tincidunt volutpat. + +Maecenas eu elit faucibus est cursus molestie nec in arcu. Suspendisse ut tristique tellus. Quisque lacinia condimentum nulla vitae ultrices. Duis ut ante nec mi pretium rutrum ac a tortor. Nunc sagittis, ante sit amet dapibus pellentesque, tellus enim egestas dui, eu cursus mi eros vitae mi. Vestibulum sollicitudin libero nunc. Maecenas tincidunt sollicitudin felis, accumsan pretium orci mollis in. Quisque in turpis quis libero rhoncus aliquet. In ligula nisi, hendrerit cursus adipiscing eget, fringilla varius dolor. Proin bibendum, purus vitae venenatis iaculis, neque nunc bibendum dolor, vel ultrices quam sem sed elit. Duis ornare, erat vitae tristique fringilla, mi mauris tempor neque, non lobortis metus massa sed ligula. + +Morbi eu congue justo. Donec turpis dui, consequat vitae ullamcorper non, faucibus sed ipsum. Nam leo nisl, euismod non iaculis eget, porta in dolor. Sed tincidunt lectus sit amet est suscipit tempus. Ut elit est, aliquet eget elementum ut, condimentum sit amet nibh. Integer velit tortor, scelerisque vel egestas in, tincidunt sed leo. Mauris vitae mi quis ante elementum auctor at porttitor diam. Aenean et felis vitae arcu dignissim tincidunt nec eu lectus. + +Donec varius convallis lectus at sodales. Fusce facilisis, nunc sed laoreet rhoncus, tortor libero interdum justo, euismod malesuada purus lorem sed lorem. Sed vitae eros consectetur nulla convallis posuere eu feugiat urna. Pellentesque nec ligula eget nulla suscipit ultrices. Suspendisse convallis neque ut erat varius a condimentum turpis dignissim. Donec eget nulla turpis. Maecenas non nisi mi, ut laoreet nisi. In nisi sapien, imperdiet et egestas id, euismod ut lorem. Pellentesque cursus mattis tempus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras malesuada lorem eget ante faucibus dapibus. Integer cursus nibh a lectus scelerisque dictum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin luctus sem sit amet leo viverra eleifend. Morbi justo urna, molestie non rutrum quis, fringilla a nisl. Aliquam sit amet iaculis nisl. + +Etiam eget ultricies nisi. Donec vel nibh mi, vel congue magna. Nulla id velit vel leo pellentesque tristique a non leo. Aliquam enim magna, faucibus at posuere sit amet, sodales vitae nulla. Pellentesque blandit elit at leo fringilla dictum pellentesque tortor adipiscing. Duis quis velit turpis, quis porta nisl. Aenean a feugiat tellus. + +Cras vitae orci at lorem cursus rhoncus a a mi. Sed egestas scelerisque egestas. Maecenas rhoncus posuere eros, ut egestas massa blandit non. Vivamus et lorem mauris, eget luctus turpis. Phasellus adipiscing sem eget libero pulvinar tempor lobortis ligula ullamcorper. Sed tellus sem, laoreet et elementum vitae, bibendum in massa. Integer rhoncus enim quis dolor vestibulum bibendum. Mauris at nunc placerat arcu suscipit tristique. Aliquam sed justo orci. Aliquam erat volutpat. + +Morbi ipsum leo, rutrum at dictum vel, mattis sed urna. Ut molestie mauris feugiat sapien elementum pellentesque. Duis a elit enim. Integer non fermentum erat. Ut non odio augue. Maecenas ultricies dignissim orci, vel faucibus risus dapibus ac. Pellentesque imperdiet lorem sed diam rutrum eu faucibus tellus commodo. Aenean sit amet dapibus nisi. Maecenas dignissim purus eget urna iaculis nec interdum nisl euismod. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla ultricies nulla id orci congue at vehicula dolor rutrum. + +Pellentesque sollicitudin sem libero, quis dictum sapien. Mauris convallis luctus dolor vitae fringilla. Nunc tristique libero eu nisi porttitor quis tempus nisl ultricies. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce lacinia aliquam metus, eget rutrum lorem facilisis auctor. Fusce urna augue, faucibus vel malesuada eu, lobortis ac dui. Praesent fringilla, nulla et faucibus luctus, quam lectus consequat est, vitae pretium orci turpis eget ante. Nunc nulla ipsum, vestibulum sed congue quis, ornare sit amet mi. Fusce at purus at ante placerat euismod vitae in augue. + +Morbi eget sapien tortor. Proin vel molestie est. Suspendisse sit amet arcu non velit blandit egestas. Quisque tincidunt ultrices purus vitae bibendum. Mauris et nibh sit amet dui convallis pharetra. Nulla laoreet ipsum et nisl ullamcorper dapibus. Fusce libero elit, viverra in facilisis et, lobortis sed erat. + +Suspendisse pellentesque elit et urna interdum id vehicula ipsum tincidunt. Nullam tellus justo, lacinia eu rhoncus sit amet, congue sed quam. Sed ornare urna vel dui porttitor non varius mauris vestibulum. Suspendisse dapibus egestas feugiat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum scelerisque hendrerit dolor quis pellentesque. Donec quis tortor eget sapien egestas pharetra. Sed commodo elit interdum sapien posuere sodales eget in erat. Quisque congue lorem neque. Nam vel adipiscing lectus. Suspendisse iaculis convallis nunc, vitae porttitor metus pellentesque vel. Proin risus nisi, pellentesque ac commodo eget, varius nec lacus. + +Aliquam eu eros ut ipsum vulputate volutpat. Etiam vulputate libero luctus metus tempor eget aliquet nibh congue. Cras commodo ornare facilisis. Sed quis arcu a nisl blandit pharetra. Quisque tristique eleifend commodo. Integer ultrices aliquam velit, sit amet fringilla nisl accumsan ut. Nam euismod ullamcorper tristique. Phasellus et neque felis. Donec posuere ante ac augue suscipit tempor. Pellentesque dapibus mollis dictum. Nulla facilisi. + +Ut arcu sem, eleifend a aliquam in, cursus sit amet lectus. Sed congue aliquam ante nec mollis. Quisque egestas faucibus sem, a lobortis elit posuere in. Nulla eu massa enim. Pellentesque a auctor arcu. Suspendisse facilisis tellus sed lectus imperdiet vitae posuere odio bibendum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec lacinia, augue et posuere eleifend, massa turpis gravida augue, adipiscing eleifend nibh purus et nunc. Suspendisse potenti. Proin pretium varius lorem, vitae tristique ante aliquet eu. Maecenas vel nisi vitae sem rutrum viverra vel sit amet elit. Phasellus mollis tortor at tellus rutrum vel molestie justo congue. Phasellus molestie sapien leo, nec lobortis nulla. + +Aliquam orci elit, hendrerit sed molestie a, luctus vitae orci. Vivamus pharetra turpis eget sapien condimentum eget vestibulum leo bibendum. In nec eros id neque commodo tempor. Aliquam erat volutpat. Nunc lobortis eleifend erat eget facilisis. Etiam molestie iaculis orci, sollicitudin laoreet orci tincidunt sit amet. Vivamus id tellus sed libero consequat vulputate ac sit amet tellus. Nulla et euismod felis. Maecenas non sapien in arcu fringilla vulputate id et dolor. Nunc arcu leo, aliquet et euismod commodo, elementum id eros. Sed tempus, augue ut malesuada molestie, eros mi egestas quam, ut tristique nisi dui eu nisl. Nullam pellentesque facilisis velit, a laoreet justo dignissim at. Morbi faucibus, diam vel dapibus varius, orci eros ultricies nunc, ac porta leo neque vulputate arcu. Vestibulum leo leo, ultrices a tincidunt sit amet, dictum at odio. + +Maecenas non pellentesque lacus. Maecenas pretium erat ut nunc viverra at dapibus ipsum placerat. Cras nec diam quis lectus bibendum porttitor id ac leo. Nam congue, massa nec volutpat bibendum, dui arcu rhoncus massa, vitae suscipit magna justo ac neque. Nunc faucibus metus ornare mi tempus aliquet. Donec eu turpis tortor, ut varius ligula. Duis in porta dui. Quisque ullamcorper semper ante interdum bibendum. + +Aliquam ullamcorper bibendum dolor eu commodo. Sed ultricies nisi vel justo facilisis non faucibus ligula porttitor. Duis in purus neque, non porta lacus. Phasellus pellentesque luctus lacus, sit amet aliquam nisl feugiat sed. Pellentesque eu metus quis nibh tempus tincidunt. Phasellus eu malesuada eros. Ut non sagittis nulla. Donec laoreet libero et tortor tincidunt imperdiet. Maecenas non nibh vitae elit ornare pellentesque. Suspendisse ac massa at felis condimentum venenatis. Etiam vestibulum hendrerit tincidunt. Etiam elementum mi a velit venenatis dapibus. Integer ut magna sit amet lacus adipiscing consequat eu nec tortor. Vivamus vitae velit quis erat varius scelerisque. Praesent tempor nunc quis risus vulputate porttitor pulvinar eu arcu. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus adipiscing nunc in ante elementum blandit. Mauris varius nibh ac turpis rutrum auctor sollicitudin velit viverra. Donec a felis vel felis ultricies auctor fringilla vitae dui. Phasellus sollicitudin, urna eget venenatis condimentum, metus metus adipiscing odio, et aliquam lorem elit non nunc. Curabitur ipsum neque, aliquet in pretium a, laoreet accumsan est. Vestibulum luctus imperdiet justo vitae adipiscing. Maecenas consectetur, dolor et suscipit pharetra, ligula dolor scelerisque mi, imperdiet ornare orci enim sed magna. Nunc porta massa sit amet nulla dapibus consectetur quis non neque. Duis et lacus lacus. Nam ultrices neque in justo ultrices at feugiat nisi venenatis. Nunc ullamcorper gravida felis quis commodo. Vestibulum pharetra lectus ut felis lacinia condimentum. Phasellus et urna ut lectus ullamcorper ullamcorper sed a urna. Donec mi eros, tristique in ultricies eu, feugiat ut arcu. + +Suspendisse sollicitudin dignissim tellus, at consectetur tellus aliquet at. Proin egestas iaculis urna, a malesuada odio imperdiet non. Fusce a varius neque. Donec volutpat, justo ac commodo aliquet, dolor dui gravida velit, at tempor lorem metus nec nunc. Suspendisse mollis, risus at blandit rutrum, diam justo blandit ante, id interdum lorem urna et ante. Nam vestibulum urna ut orci tincidunt auctor. Suspendisse potenti. Nam non laoreet neque. Integer eget magna eget quam faucibus vulputate. Donec a facilisis justo. Ut consectetur purus vel enim faucibus elementum. Donec lacinia aliquam lorem ac pharetra. Integer viverra aliquam neque, id malesuada leo euismod ut. Donec lobortis, lorem nec ornare mollis, lacus turpis tincidunt felis, sit amet placerat nisl mi nec risus. + +Quisque in magna nulla. Vestibulum ullamcorper neque in mi eleifend at bibendum metus ornare. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam tincidunt risus cursus quam fringilla ornare. Quisque rutrum gravida tellus, quis suscipit metus vehicula id. Pellentesque libero nulla, aliquet ut porta at, pretium fringilla neque. Curabitur sagittis mi felis, eget dapibus felis. Nulla eros nulla, rutrum id sollicitudin vel, ornare sit amet dolor. In hac habitasse platea dictumst. Vestibulum venenatis felis porta magna bibendum in euismod lectus rutrum. Etiam ante sapien, lobortis quis venenatis feugiat, posuere a leo. Vestibulum ullamcorper nibh nec nulla imperdiet hendrerit. Etiam at lorem sit amet nunc pretium bibendum quis ut enim. In hac habitasse platea dictumst. Aenean vulputate, velit quis mattis mattis, neque justo posuere tortor, eget sollicitudin purus nunc at lacus. Phasellus egestas volutpat neque, at ultrices dui lobortis et. + +Proin fringilla turpis et lectus aliquet ac tempus est sagittis. In eu felis felis. Nam dui eros, rhoncus vel aliquam quis, pharetra vitae nibh. Maecenas aliquam nibh a ante hendrerit rhoncus. Cras consectetur, lorem dictum auctor pretium, libero augue imperdiet lorem, a vulputate ante dui ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus dapibus augue a nisi fringilla vitae fermentum ipsum placerat. Praesent feugiat pharetra ipsum, id hendrerit elit interdum ut. Nullam eu feugiat enim. Aenean sem magna, dictum nec posuere quis, tempor sed enim. Duis in rutrum felis. + +Proin convallis cursus mattis. Suspendisse potenti. Nam vehicula egestas ligula ac lobortis. Maecenas semper gravida libero sit amet consectetur. Nam faucibus congue nulla, eu dignissim erat aliquam sit amet. Phasellus venenatis velit id quam convallis posuere. Sed sollicitudin neque ut magna laoreet sit amet porta enim tincidunt. + +Phasellus leo lectus, dictum sed semper posuere, varius rhoncus metus. Quisque ipsum dui, viverra eget rutrum at, aliquam non risus. Nulla metus justo, sodales quis accumsan ac, consectetur sed mi. Etiam viverra massa eget ipsum porta ac fermentum libero semper. Nam fringilla tempor sem et auctor. Nulla facilisi. Donec et metus in nisl accumsan consequat vel aliquet diam. Fusce placerat tincidunt nulla, et dictum diam pretium sed. Phasellus nibh magna, consequat sed ornare sed, bibendum ut lectus. Integer at dolor sed dui aliquet pulvinar. Aliquam egestas pulvinar urna non sollicitudin. Maecenas eget justo quam. + +Morbi quis rutrum risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi fermentum elit sit amet metus aliquam dictum. Phasellus tincidunt convallis venenatis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In luctus sagittis mi, vitae condimentum eros elementum quis. Suspendisse potenti. Mauris cursus, sem eu malesuada pulvinar, nisl ipsum venenatis orci, non tempor tellus tortor et velit. Vestibulum consequat vestibulum porta. Donec aliquam tellus id arcu porttitor ut rhoncus arcu egestas. Donec sit amet erat risus, id aliquet elit. In arcu leo, gravida quis tempus eu, convallis sed leo. Vivamus lobortis adipiscing justo vitae gravida. Proin vitae fringilla dolor. + +Curabitur mollis, quam quis bibendum iaculis, massa quam sollicitudin erat, id tempus massa libero nec augue. Vestibulum vel nisl et ante mollis eleifend. Maecenas non velit eu tortor ullamcorper hendrerit. Vestibulum mauris leo, varius eu gravida nec, tempor sed risus. Nunc posuere velit auctor dui pulvinar bibendum. Nulla non vulputate nulla. Cras a ante ut lacus porta tristique ac at mauris. Duis eu mi libero. Nullam volutpat aliquet turpis at malesuada. Maecenas laoreet ultricies ullamcorper. Nullam tempor tortor ac enim convallis sed porta mauris laoreet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque imperdiet fermentum lectus eget commodo. Proin ultrices lacus a dui pharetra sit amet laoreet ipsum malesuada. Cras pulvinar turpis sed purus molestie ornare. Duis semper tellus in dolor tristique ut placerat felis aliquet. + +Donec ac magna diam, sed aliquet erat. Donec dui augue, mollis sit amet molestie in, facilisis vel erat. Nunc congue mollis dolor vel dapibus. Suspendisse ut sodales massa. Morbi in rhoncus odio. Ut vehicula enim a tellus porta vulputate. Aliquam lorem velit, tempor a egestas eu, sollicitudin at diam. Etiam facilisis ante ac felis gravida sit amet elementum turpis vestibulum. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris sit amet tortor et neque scelerisque egestas. Morbi nec nunc orci, a luctus ipsum. + +Phasellus in enim eu lectus condimentum semper eu in lectus. Sed ac enim nisi, et semper mauris. In pellentesque tincidunt arcu, quis malesuada urna consectetur ut. Donec consectetur imperdiet nulla ac fringilla. Aenean et nibh mi. Suspendisse volutpat auctor odio et euismod. Pellentesque congue elit ante, at tristique urna. + +Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum sit amet ipsum nunc, non blandit sem. Integer tempor hendrerit pretium. Vestibulum id leo a nunc tempus pellentesque. Morbi vel velit ac lectus consectetur convallis. Donec iaculis, leo ac fringilla commodo, quam turpis eleifend arcu, eu eleifend nisi purus at tellus. Sed sit amet erat ut mauris tristique consectetur quis vel enim. Proin aliquet ornare posuere. Cras quis odio erat. Vestibulum pretium commodo commodo. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus consectetur mauris id purus venenatis non convallis diam porta. Nullam sagittis volutpat interdum. Aenean diam lacus, blandit vel semper at, varius in est. Suspendisse et magna diam, et scelerisque mauris. Donec molestie nisl ac leo vehicula sed rhoncus metus viverra. Donec a dui leo, non egestas tellus. Donec sagittis pulvinar ante, ut vehicula ante congue non. Nullam aliquet rhoncus lorem ac venenatis. In ut lectus dolor, ac imperdiet diam. Nunc pellentesque cursus ante sed pretium. Nam vulputate pellentesque nisi id rhoncus. Donec in magna tellus. + +Vestibulum iaculis sem sit amet nulla auctor viverra. Proin in turpis id libero molestie tincidunt eu eu nisl. Phasellus vulputate lectus a massa malesuada sed aliquet libero egestas. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin eu turpis mauris. Duis interdum velit et erat faucibus ut faucibus est gravida. Proin interdum massa ac libero dictum vel dignissim ante vehicula. Donec ut metus in mauris fringilla porttitor vitae ut urna. Nulla sodales pellentesque interdum. Vivamus eget metus nisi. Phasellus metus diam, mattis id tristique vitae, ullamcorper dictum dolor. Ut tincidunt gravida turpis, nec ornare tortor laoreet at. Vivamus eget est sed erat pharetra porttitor non eu mauris. Mauris augue lorem, tristique sed rutrum in, bibendum faucibus odio. + +Phasellus elit felis, luctus non tristique vel, hendrerit vitae ipsum. Cras aliquet, lectus at ultrices sollicitudin, odio nisl ullamcorper arcu, vel tincidunt dolor turpis id enim. Phasellus lacus dui, condimentum interdum blandit eu, gravida nec urna. Donec rhoncus fringilla diam sit amet dictum. Mauris sed sapien non velit vestibulum ultricies eu in nulla. Etiam quis euismod dui. Praesent vel dolor nisi. Fusce leo neque, interdum et feugiat varius, congue eu nisl. Cras vehicula, ipsum id vulputate scelerisque, massa tellus auctor mi, in sagittis odio mauris ac ipsum. Nunc sed velit felis. Proin vitae eros tortor, a interdum ante. Etiam condimentum libero mauris, nec dictum augue. + +Phasellus sit amet dui eget nisl aliquet euismod. Nam mauris enim, volutpat pretium interdum non, sollicitudin tempor est. Suspendisse dictum consectetur lacinia. Pellentesque aliquet eleifend aliquam. Sed adipiscing libero a magna venenatis in dapibus tortor euismod. Phasellus vestibulum, ante id feugiat consectetur, tellus enim sagittis arcu, sit amet posuere nisi eros vitae turpis. Quisque in ante ligula, sed egestas nibh. Fusce rutrum, risus vitae consectetur scelerisque, velit erat dictum est, quis vestibulum diam neque et sapien. Quisque tincidunt fermentum pellentesque. Donec non turpis at eros porttitor suscipit. Duis mollis auctor libero, scelerisque volutpat dui interdum at. + +Etiam vitae sapien eget leo ultrices accumsan quis sit amet est. Quisque volutpat interdum ipsum, quis porta est malesuada at. Nam risus orci, mattis id lobortis eget, vulputate ac augue. Ut quis metus nec nisi pellentesque luctus quis in nunc. Nam vitae velit eu turpis rhoncus placerat at id velit. In hac habitasse platea dictumst. Phasellus diam turpis, scelerisque vel faucibus in, laoreet nec nulla. Suspendisse justo quam, molestie in dapibus dapibus, tempus sit amet dolor. Praesent vitae gravida quam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis tristique elit ac augue sodales tristique. Ut aliquam tristique placerat. Sed purus neque, interdum et hendrerit in, feugiat a justo. Fusce id ullamcorper tellus. + +Nulla ipsum mi, vulputate convallis aliquam non, fringilla vulputate velit. Curabitur ullamcorper dapibus neque, vitae congue magna tempor ac. Proin volutpat, nisi ac hendrerit consequat, augue tortor congue mi, non molestie elit ipsum vel augue. Ut vestibulum feugiat nisi, vulputate semper erat rutrum quis. Maecenas adipiscing, magna a ultrices volutpat, velit risus adipiscing purus, et condimentum orci nulla ut nisl. Aenean lorem metus, imperdiet vel eleifend sit amet, placerat vitae enim. Duis turpis dui, pulvinar ut consectetur tincidunt, tristique vitae tortor. + +Donec mattis tincidunt leo, ac sodales elit vulputate sed. Etiam vel nunc at sem accumsan tincidunt. Nullam vel est sit amet arcu egestas viverra at luctus sem. Aliquam convallis, turpis vel sagittis mattis, nisl nibh ultrices quam, et dignissim leo velit quis mi. Fusce odio ligula, vehicula eget iaculis eget, sollicitudin at augue. Duis vel urna leo. Sed nec ante metus. Quisque pellentesque metus sit amet velit vulputate vel ullamcorper orci mattis. In sed nulla eget enim posuere iaculis eu vel metus. Vivamus tempus commodo dapibus. Suspendisse potenti. Pellentesque massa nulla, dictum eget ultrices vel, tempor vel velit. Integer sit amet semper lacus. + +Integer vehicula tortor elit. Sed euismod venenatis tortor, nec interdum risus pretium ultrices. Nullam rhoncus lobortis metus, sodales rutrum felis tincidunt eu. Nulla ornare rhoncus imperdiet. Mauris euismod massa eget augue lobortis imperdiet quis sed tortor. Vestibulum at risus ut turpis venenatis faucibus. Nunc ultrices dignissim volutpat. Integer magna dolor, rhoncus ut ultrices ac, pharetra eu felis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur faucibus dolor non mauris placerat interdum. Morbi quis lectus nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Quisque ut erat odio. In nulla nibh, volutpat eget faucibus in, rhoncus nec tortor. Aenean at lacus odio. Integer scelerisque lobortis rutrum. Aliquam erat volutpat. Pellentesque sed eleifend purus. Ut ac vehicula risus. Cras ut sollicitudin ante. In turpis mauris, bibendum et sagittis id, sollicitudin et dui. Phasellus non magna non sem dictum posuere in id metus. Quisque sapien tellus, pellentesque nec elementum ut, luctus mattis nisl. + +Maecenas sodales posuere risus, in fringilla eros adipiscing eget. Pellentesque leo metus, consequat nec interdum sed, facilisis ac lorem. Curabitur facilisis euismod consectetur. Vivamus feugiat magna ut sapien tincidunt et egestas ante pharetra. Suspendisse potenti. Nulla id dui risus, ut porta mauris. Cras aliquet purus ac leo consequat quis aliquam orci sollicitudin. Proin sapien ante, ullamcorper blandit iaculis vel, vestibulum nec eros. Sed euismod purus quis elit varius a fermentum felis cursus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut enim massa, mollis sed semper eu, lobortis et libero. Sed porttitor mattis nunc nec facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus feugiat, erat et laoreet placerat, est leo malesuada justo, accumsan rhoncus lorem risus a libero. Nam posuere, magna in rhoncus auctor, justo magna euismod urna, auctor laoreet urna sem et magna. + +Aliquam suscipit ligula porta felis interdum posuere. Phasellus metus turpis, vehicula vitae ornare ut, molestie et quam. Nulla ultricies gravida eros, sit amet molestie metus vulputate in. Fusce ornare quam sollicitudin dolor venenatis ut ultricies tellus luctus. Ut eget placerat est. Aliquam rhoncus venenatis lectus, quis pretium lacus accumsan ut. Proin velit turpis, imperdiet non auctor non, congue vitae lorem. Nulla sit amet dui et augue eleifend bibendum. Donec sit amet est vel enim semper consectetur. Nulla venenatis pharetra dui a consequat. Nulla vitae lacus a mauris scelerisque scelerisque sed at magna. Mauris sed turpis ac odio consectetur lobortis. + +Suspendisse id enim sit amet eros pellentesque aliquam nec a nunc. Phasellus sit amet metus id felis porttitor pharetra. Nulla ligula eros, posuere id condimentum faucibus, dictum eget nulla. Pellentesque elementum porttitor lectus, id volutpat orci posuere non. In fermentum, orci non lobortis porttitor, velit erat viverra purus, sed vehicula augue purus vel justo. Nullam a orci eros. Mauris dapibus sollicitudin erat ut volutpat. + +Aenean fringilla sagittis est eu cursus. Pellentesque aliquam massa nec nulla tristique ut vulputate odio convallis. Nullam lobortis, libero nec rhoncus porta, risus erat ultricies elit, et faucibus eros massa id neque. Fusce blandit laoreet arcu id vestibulum. Maecenas quam enim, malesuada nec lobortis non, laoreet nec metus. Vestibulum elementum quam in neque fermentum tempor. Aenean euismod egestas lacus. Morbi vulputate, ligula ac lobortis auctor, elit mi euismod est, id ornare libero velit sed nibh. Sed magna turpis, facilisis vitae fringilla et, ullamcorper ac nisi. + +In quis lacus id nisl pulvinar vulputate. Donec nec ligula ut dui aliquam pulvinar in ac erat. Sed nec turpis sit amet mi imperdiet dapibus. Vivamus quis euismod nunc. Mauris quis sem sed nisi vulputate pharetra. Aliquam tempor odio quis dolor accumsan gravida. Donec a risus non tellus sagittis molestie id nec leo. Pellentesque commodo volutpat arcu nec pharetra. Maecenas sagittis suscipit elit et venenatis. Vivamus consequat, lorem eget vehicula pharetra, nulla mauris elementum enim, sit amet porta orci sem quis nisl. Nullam non sagittis ante. Praesent sapien ipsum, elementum sed placerat sed, pretium at ante. + +Vestibulum pellentesque dapibus magna ac dictum. Sed quis mattis erat. Nam pulvinar malesuada erat dignissim facilisis. Fusce id erat a velit laoreet pulvinar. Pellentesque mollis tristique neque, ut pharetra arcu facilisis sit amet. Vivamus rhoncus hendrerit volutpat. Morbi elementum viverra velit et interdum. Sed id nunc eu arcu vehicula pulvinar. Mauris facilisis malesuada purus non accumsan. Quisque tincidunt, dolor nec commodo fermentum, neque neque consequat lacus, sed imperdiet eros massa eu diam. Nullam pellentesque blandit magna tempus bibendum. + +Mauris ut ipsum id dui feugiat iaculis. Donec in justo nec felis egestas tempor. Nullam sollicitudin dapibus tristique. Quisque tortor orci, hendrerit ac vestibulum sed, aliquet sed elit. Nunc eget nisi sapien. Cras erat ipsum, tincidunt et tincidunt in, laoreet id erat. Nulla sed lorem et turpis congue pharetra sit amet vel lacus. Aliquam in eros a ipsum auctor pellentesque ut et libero. Ut facilisis, lacus eget vulputate luctus, neque dui mollis lacus, non porttitor ipsum tellus in quam. Vestibulum sodales tempus nulla, eget euismod neque dapibus laoreet. Aliquam ultrices tempor ornare. Vestibulum tempus, arcu sed rutrum condimentum, tortor urna volutpat tortor, a blandit lorem quam a lorem. + +Suspendisse vitae tortor et massa hendrerit sagittis. Vivamus condimentum ligula a risus venenatis eleifend dignissim nibh auctor. Integer feugiat tellus ornare metus aliquet ac luctus mauris eleifend. Vivamus nec purus ac purus tincidunt accumsan et vel ligula. In convallis diam eu enim ultrices eu iaculis eros blandit. Suspendisse nibh arcu, sagittis id consectetur in, hendrerit nec tellus. Quisque vitae purus id nisi consequat volutpat quis eu odio. Aliquam erat volutpat. Quisque quam elit, ornare eu volutpat et, fermentum nec dui. Phasellus eleifend velit non diam congue iaculis convallis sit amet nisi. Donec sed felis urna. Quisque cursus lectus sed nibh mollis eu imperdiet risus pellentesque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean pretium ultrices mi, eget luctus nisl condimentum non. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In eu erat dolor, nec dapibus sem. + +Donec ut purus a enim placerat dictum. Praesent euismod libero ac urna commodo convallis. Praesent sagittis diam vel neque feugiat fringilla pharetra nisl iaculis. Ut placerat lacinia lacus. Nunc pharetra venenatis augue, id venenatis orci tristique eget. Suspendisse et mauris dolor. Suspendisse euismod est et purus blandit faucibus. Praesent placerat gravida massa ac gravida. Nunc eu elit nunc, ac pulvinar dolor. + +In adipiscing, lectus molestie commodo sodales, augue dui sagittis velit, mattis commodo ipsum sapien id tellus. Morbi pellentesque, dolor sit amet aliquam mollis, nibh nibh auctor massa, id bibendum ante tortor eget leo. Suspendisse semper blandit imperdiet. Nam semper pulvinar risus, id scelerisque velit gravida ac. Vivamus a mauris tellus. Aliquam turpis justo, eleifend id iaculis in, sollicitudin non ante. Fusce ac mauris elit, at hendrerit augue. Morbi pretium mollis felis. + +Vivamus eleifend posuere lacus, vel suscipit dui molestie quis. Nunc aliquet risus et nibh placerat ut laoreet urna congue. In sit amet leo est, vitae sollicitudin dolor. Donec eget urna sit amet erat molestie tempus. Sed facilisis sem nibh. Donec consequat arcu nec turpis pharetra euismod. Aenean ac nisi diam. Aliquam gravida sodales nibh, id elementum diam gravida et. Duis aliquet ligula sed nunc aliquet varius. Integer posuere sollicitudin lacus. Quisque in odio leo, a hendrerit quam. + +Morbi tellus dolor, aliquam vel tristique vitae, auctor a enim. Fusce arcu odio, fermentum in cursus a, convallis in arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer porttitor pretium nunc, vel sagittis enim pulvinar et. Aenean vel nisl sit amet erat posuere sollicitudin. Aliquam sed enim et ligula hendrerit volutpat. Duis iaculis venenatis quam nec auctor. Mauris at orci neque. Aenean a lorem nisl. Praesent quam ipsum, hendrerit vitae bibendum id, cursus nec ipsum. + +Morbi vulputate justo in urna rhoncus in volutpat mauris rutrum. Integer dui arcu, elementum et viverra at, vehicula ultrices tortor. Fusce non justo vitae nulla rhoncus venenatis. Curabitur quis tincidunt mauris. Nunc vitae sapien in arcu dapibus lacinia vel eu ante. Quisque nibh ante, tristique at sagittis at, imperdiet ut massa. Etiam lacus ipsum, euismod vel fermentum ut, condimentum non nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur mauris libero, fringilla et imperdiet sit amet, volutpat non velit. Sed non purus ligula. Praesent commodo semper enim quis egestas. Nullam nec leo mi. Cras semper ornare porttitor. Ut lorem libero, sollicitudin in sollicitudin eu, dictum vel nulla. Donec et tempor elit. + +Aliquam tincidunt odio sit amet ligula interdum ac gravida odio cursus. In tellus mi, pellentesque eget varius a, gravida dapibus metus. Phasellus non nulla sed diam eleifend tempor. Morbi venenatis interdum justo, nec volutpat enim hendrerit sed. Morbi tortor felis, ullamcorper et auctor quis, ullamcorper a metus. Donec posuere, sem quis luctus aliquet, lacus sapien pulvinar turpis, sit amet suscipit augue justo id arcu. Aliquam diam tellus, malesuada non porta in, ultricies quis tortor. Vestibulum malesuada molestie porta. Praesent placerat leo vel velit pretium et aliquet lorem vestibulum. Nunc id leo arcu. Mauris vel faucibus metus. Curabitur ornare, quam et imperdiet adipiscing, urna magna imperdiet mi, eget ultrices arcu diam eu nisi. Suspendisse potenti. Nunc elementum, magna scelerisque ornare rhoncus, massa erat ultrices purus, id ultrices ante ipsum in ligula. + +Integer ut leo turpis, sed accumsan elit. Morbi egestas malesuada elementum. Etiam ut urna nisl. Morbi consectetur auctor dui, ornare imperdiet diam dignissim in. Cras adipiscing laoreet erat et sodales. Nunc malesuada purus sed diam sollicitudin auctor. In congue mauris sed dolor aliquam hendrerit rutrum odio scelerisque. Nam non leo mi. Pellentesque accumsan consequat porttitor. Etiam placerat accumsan rutrum. Praesent ante risus, molestie et molestie a, condimentum eu nunc. Integer pulvinar augue a justo bibendum fringilla. Nulla facilisi. Fusce vitae libero risus, quis feugiat turpis. Nulla ullamcorper convallis mauris, id suscipit nibh molestie sed. Integer sit amet nulla vitae lorem porta mattis a ut lectus. + +Maecenas sodales, justo nec euismod sodales, mi turpis adipiscing neque, vel feugiat justo magna eu quam. Morbi aliquam pharetra nisi mattis auctor. Donec lorem odio, consequat a mollis porta, fringilla nec elit. Cras nulla dui, facilisis nec vestibulum non, pulvinar at enim. Phasellus in purus turpis, non mattis eros. Nunc commodo justo congue magna molestie sed fermentum erat aliquam. Pellentesque eu ipsum urna. Duis sit amet velit mi, id fermentum purus. Integer sed ligula lobortis ipsum aliquam dignissim vel ullamcorper felis. + +Nulla felis nisi, molestie vel eleifend dictum, varius eget nulla. Nulla facilisi. Proin accumsan dui at nisi laoreet sed malesuada ante sodales. Aenean nec massa at nibh porttitor placerat. Donec lacinia blandit mauris a euismod. In varius aliquet varius. Mauris at massa lacus, ac bibendum sapien. + +Nunc elit libero, suscipit id ultricies ac, sodales ut libero. Curabitur dignissim leo orci. Sed eget lacus ac orci vulputate luctus. Aliquam posuere, ligula nec elementum dictum, enim nibh ornare lacus, quis pretium ante elit a dui. In at dui libero, quis tincidunt dolor. Nulla sed sapien justo. Suspendisse potenti. + +Praesent fermentum posuere mauris nec egestas. Sed porta pharetra fermentum. Donec imperdiet nisi ut tortor ultricies tincidunt. Fusce tellus diam, faucibus a tincidunt in, mollis at lorem. Curabitur vehicula condimentum semper. Vivamus cursus vestibulum dolor aliquet mollis. Sed ornare convallis metus, nec venenatis justo elementum eget. Nam tempor tincidunt lacus ut molestie. Quisque sit amet diam quis dolor laoreet viverra. Donec nisi leo, pharetra id dapibus ac, tempus eget massa. + +Cras arcu velit, molestie id vestibulum ut, ultrices placerat elit. Nulla scelerisque luctus velit, non tempus turpis posuere at. Morbi ornare, est non pharetra placerat, neque eros adipiscing diam, non tincidunt mauris mi quis urna. Nullam aliquet lobortis faucibus. Duis sodales vehicula velit eget pharetra. Integer hendrerit, est in dictum eleifend, massa risus volutpat dolor, ut consectetur nibh nibh vel eros. Morbi non quam tellus. In hac habitasse platea dictumst. Integer nisi enim, gravida vel pharetra non, mollis id mauris. + +Ut adipiscing massa nec lectus pretium vestibulum. Proin sit amet lacus vel leo ultrices ornare. Vestibulum lobortis elit quis odio scelerisque et ultrices dolor condimentum. Vestibulum tincidunt bibendum felis, a suscipit lectus sagittis a. Proin id dolor a magna vulputate blandit vitae vitae ipsum. Suspendisse quis quam nulla, ultricies interdum arcu. Suspendisse potenti. Ut a velit eget erat congue varius. Ut non ligula purus, sed tincidunt urna. + +Aenean turpis purus, bibendum placerat aliquam at, pretium eget nisi. Proin dui metus, blandit nec placerat eu, luctus ac enim. Proin nibh turpis, consectetur id lacinia sit amet, cursus eu neque. Curabitur commodo vulputate justo, vel sodales sapien mattis non. Nam et diam est. Nulla quam lorem, porttitor fermentum luctus in, vestibulum in justo. Nulla et mi id diam scelerisque sodales. In iaculis luctus urna, in accumsan augue eleifend vulputate. + +Pellentesque accumsan sem nec sapien consectetur id eleifend risus pharetra. Sed eu libero a mi imperdiet blandit. Nam pretium pharetra odio vitae interdum. Etiam auctor, massa id feugiat congue, ante diam laoreet diam, porta suscipit diam lacus eget nisl. Integer arcu justo, bibendum eu vehicula vel, ullamcorper vitae ligula. Proin et leo vitae sem luctus placerat vel sit amet justo. Suspendisse nec pretium libero. Donec tempus, augue ac porta faucibus, magna mi luctus sapien, vitae dignissim tortor lacus at ipsum. Donec eleifend, mauris at gravida condimentum, mauris sapien consequat lorem, a feugiat risus quam at purus. Nullam sed magna id velit imperdiet luctus. Aenean eu neque sit amet dolor pulvinar placerat. Aenean a ligula metus. Curabitur id arcu massa. Phasellus tortor sem, ornare sed tincidunt quis, varius eu orci. Sed luctus consequat leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Mauris eu magna libero. Pellentesque adipiscing mi eu augue vestibulum tristique. Donec at augue in erat viverra cursus non tristique ligula. Suspendisse vulputate sollicitudin lectus, eu faucibus risus hendrerit eu. Aenean ut tortor quis ipsum euismod accumsan. Pellentesque commodo pellentesque laoreet. Integer cursus, magna eu placerat pellentesque, risus enim sodales risus, quis ornare orci nisl quis orci. + +Nunc elementum lectus nec nibh iaculis sagittis. Praesent sodales justo sit amet risus lacinia ac varius sapien sodales. Praesent mollis quam diam, a molestie risus. Phasellus ac varius urna. Duis lobortis commodo nisi, eu dignissim nulla rutrum quis. Sed quis elit lacus. Vivamus ullamcorper commodo justo, ac pellentesque nunc venenatis at. Vivamus volutpat congue eleifend. Morbi sed augue lacus, eu venenatis ligula. Donec imperdiet libero ut sapien suscipit ullamcorper vitae nec metus. Donec massa purus, posuere eu gravida quis, ultricies sed purus. + +Aenean accumsan consequat arcu in dignissim. Donec tristique feugiat elit id fringilla. Proin ac ipsum eros, ac tincidunt erat. Nunc non orci ac nibh dapibus laoreet ut eget mauris. Nulla accumsan lacinia dui, sit amet porta magna luctus quis. Nam purus nisi, commodo vitae molestie non, rhoncus non ligula. Aenean scelerisque ultricies sodales. Cras quis hendrerit eros. In et massa erat, eu sagittis sapien. + +Sed bibendum cursus mauris, vel venenatis quam ornare vitae. Aliquam ullamcorper, est ut bibendum condimentum, nulla nulla auctor elit, eget pulvinar eros nibh id libero. Nulla pellentesque, mauris vel rutrum accumsan, quam felis faucibus ligula, ut blandit elit sem id eros. Donec eu libero mauris. Phasellus ante diam, commodo non auctor a, gravida ac nisl. Curabitur mattis iaculis nisl, non dapibus odio viverra ac. Mauris ornare imperdiet lacus sed pellentesque. Cras in est mauris. Duis pharetra felis nec ligula consectetur viverra. Maecenas eu mi et lorem sagittis rutrum eget nec turpis. Cras feugiat consectetur dictum. Nullam sit amet leo mauris. Mauris neque sem, facilisis at rutrum sed, rutrum a ipsum. \ No newline at end of file From 263a84154e974c3e8691d2d894af9f841f6f109c Mon Sep 17 00:00:00 2001 From: brmeyer Date: Fri, 7 Dec 2012 11:16:48 -0500 Subject: [PATCH 23/58] HHH-7840 org.hibernate.id.IncrementGenerator very slow --- .../src/main/java/org/hibernate/id/IncrementGenerator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/id/IncrementGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/IncrementGenerator.java index 728d8364af..a116034566 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IncrementGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IncrementGenerator.java @@ -53,6 +53,7 @@ import org.hibernate.type.Type; * * @author Gavin King * @author Steve Ebersole + * @author Brett Meyer */ public class IncrementGenerator implements IdentifierGenerator, Configurable { @@ -103,7 +104,7 @@ public class IncrementGenerator implements IdentifierGenerator, Configurable { for ( int i=0; i < tables.length; i++ ) { final String tableName = dialect.quote( normalizer.normalizeIdentifierQuoting( tables[i] ) ); if ( tables.length > 1 ) { - buf.append( "select " ).append( column ).append( " from " ); + buf.append( "select max(" ).append( column ).append( ") as mx from " ); } buf.append( Table.qualify( catalog, schema, tableName ) ); if ( i < tables.length-1 ) { @@ -112,7 +113,7 @@ public class IncrementGenerator implements IdentifierGenerator, Configurable { } if ( tables.length > 1 ) { buf.insert( 0, "( " ).append( " ) ids_" ); - column = "ids_." + column; + column = "ids_.mx"; } sql = "select max(" + column + ") from " + buf.toString(); From 79cc807aae892826cbb0b2e119160e33081851ee Mon Sep 17 00:00:00 2001 From: brmeyer Date: Fri, 7 Dec 2012 11:28:16 -0500 Subject: [PATCH 24/58] HHH-7839 Documentation bug at SimpleNaturalIdLoadAccess.getReference(Object naturalIdValue) --- .../src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java b/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java index 0d4da07705..89900922a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java @@ -65,7 +65,7 @@ public interface SimpleNaturalIdLoadAccess { * * @param naturalIdValue The value of the natural-id for the entity to retrieve * - * @return the persistent instance or proxy + * @return The persistent instance or proxy, if an instance exists. Otherwise, {@code null}. */ public Object getReference(Object naturalIdValue); From 18d10d6fde7c1cfe83ec94ee3f32dec24c7b01af Mon Sep 17 00:00:00 2001 From: Barney Date: Tue, 27 Nov 2012 13:34:07 +0100 Subject: [PATCH 25/58] HHH-7826 - Generate 'unique' constraints in stable order --- .../src/main/java/org/hibernate/mapping/Table.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 3b34475107..971154eb25 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -55,9 +55,9 @@ public class Table implements RelationalModel, Serializable { private Map columns = new LinkedHashMap(); private KeyValue idValue; private PrimaryKey primaryKey; - private Map indexes = new HashMap(); - private Map foreignKeys = new HashMap(); - private Map uniqueKeys = new HashMap(); + private Map indexes = new LinkedHashMap(); + private Map foreignKeys = new LinkedHashMap(); + private Map uniqueKeys = new LinkedHashMap(); private final int uniqueInteger; private boolean quoted; private boolean schemaQuoted; From 47ddaad98d4c04b7c3043c032d3eb7c6363a2750 Mon Sep 17 00:00:00 2001 From: Kamyar Sajjadi Date: Sun, 2 Dec 2012 14:20:06 +0100 Subject: [PATCH 26/58] HHH-7813 - Fixed the mistakes in code examples. All the examples was in chapter 4 (Batch processing). --- .../src/main/docbook/devguide/en-US/extras/executeUpdate.java | 4 ++-- .../src/main/docbook/devguide/en-US/extras/hql-insert.java | 4 ++-- .../src/main/docbook/devguide/en-US/extras/hql_delete.java | 4 ++-- .../main/docbook/devguide/en-US/extras/updating_version.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/documentation/src/main/docbook/devguide/en-US/extras/executeUpdate.java b/documentation/src/main/docbook/devguide/en-US/extras/executeUpdate.java index 632b2f2a3b..ce05fc09a2 100644 --- a/documentation/src/main/docbook/devguide/en-US/extras/executeUpdate.java +++ b/documentation/src/main/docbook/devguide/en-US/extras/executeUpdate.java @@ -3,9 +3,9 @@ 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 ) +int updatedEntities = session.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); -session.close(); \ No newline at end of file +session.close(); diff --git a/documentation/src/main/docbook/devguide/en-US/extras/hql-insert.java b/documentation/src/main/docbook/devguide/en-US/extras/hql-insert.java index c1ec22cc49..9505919c3a 100644 --- a/documentation/src/main/docbook/devguide/en-US/extras/hql-insert.java +++ b/documentation/src/main/docbook/devguide/en-US/extras/hql-insert.java @@ -2,7 +2,7 @@ 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 ) +int createdEntities = session.createQuery( hqlInsert ) .executeUpdate(); tx.commit(); -session.close(); \ No newline at end of file +session.close(); diff --git a/documentation/src/main/docbook/devguide/en-US/extras/hql_delete.java b/documentation/src/main/docbook/devguide/en-US/extras/hql_delete.java index d4720e9d82..87c2971706 100644 --- a/documentation/src/main/docbook/devguide/en-US/extras/hql_delete.java +++ b/documentation/src/main/docbook/devguide/en-US/extras/hql_delete.java @@ -3,8 +3,8 @@ 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 ) +int deletedEntities = session.createQuery( hqlDelete ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); -session.close(); \ No newline at end of file +session.close(); diff --git a/documentation/src/main/docbook/devguide/en-US/extras/updating_version.java b/documentation/src/main/docbook/devguide/en-US/extras/updating_version.java index b59ff60f1d..b68c41c370 100644 --- a/documentation/src/main/docbook/devguide/en-US/extras/updating_version.java +++ b/documentation/src/main/docbook/devguide/en-US/extras/updating_version.java @@ -1,9 +1,9 @@ Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName"; -int updatedEntities = s.createQuery( hqlUpdate ) +int updatedEntities = session.createQuery( hqlUpdate ) .setString( "newName", newName ) .setString( "oldName", oldName ) .executeUpdate(); tx.commit(); -session.close(); \ No newline at end of file +session.close(); From d34e409f416fda98893c85ce6bb30feb88f000f9 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Fri, 7 Dec 2012 12:12:49 -0500 Subject: [PATCH 27/58] HHH-7811 grammar error in devguide for HB --- documentation/src/main/docbook/devguide/en-US/Locking.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/docbook/devguide/en-US/Locking.xml b/documentation/src/main/docbook/devguide/en-US/Locking.xml index 96f61fafd3..492adba889 100644 --- a/documentation/src/main/docbook/devguide/en-US/Locking.xml +++ b/documentation/src/main/docbook/devguide/en-US/Locking.xml @@ -15,7 +15,7 @@ Optimistic - Optimistic locking ssumes that multiple transactions can complete without affecting each other, and that + Optimistic locking assumes that multiple transactions can complete without affecting each other, and that therefore transactions can proceed without locking the data resources that they affect. Before committing, each transaction verifies that no other transaction has modified its data. If the check reveals conflicting modifications, the committing transaction rolls back. From afc49f435ffb6039205cad6a3e6a758ba950217d Mon Sep 17 00:00:00 2001 From: Shawn Clowater Date: Thu, 15 Nov 2012 10:01:32 -0700 Subject: [PATCH 28/58] Changed the error handling for LazyInitializationException to go through the throwLazyInitializationException method so that the collection role is reported. --- .../collection/internal/AbstractPersistentCollection.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java index 75323d23d6..4ed942faf1 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java @@ -183,7 +183,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers isTempSession = true; } else { - throw new LazyInitializationException( "could not initialize proxy - no Session" ); + throwLazyInitializationException( "could not initialize proxy - no Session" ); } } else if ( !session.isOpen() ) { @@ -193,7 +193,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers isTempSession = true; } else { - throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" ); + throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" ); } } else if ( !session.isConnected() ) { @@ -203,7 +203,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers isTempSession = true; } else { - throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); + throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); } } From 02825e9fd66be189fd94a9fa1f40c9d852cfc6ae Mon Sep 17 00:00:00 2001 From: mgrenonville Date: Fri, 7 Dec 2012 17:40:51 +0100 Subject: [PATCH 29/58] HHH-7849 Unable to join on an embedded field --- .../hql/internal/ast/tree/ComponentJoin.java | 3 +- .../test/component/basic2/Component.java | 39 +++++++++++++++++++ .../component/basic2/ComponentJoinsTest.java | 28 +++++++++---- 3 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java index cb152908dc..29099a5fe9 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java @@ -57,7 +57,8 @@ public class ComponentJoin extends FromElement { fromClause.addJoinByPathMap( componentPath, this ); initializeComponentJoin( new ComponentFromElementType( this ) ); - this.columns = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty ); + String[] cols = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty ); + this.columns = cols.length == 0 ? new String[] { null} : cols; StringBuilder buf = new StringBuilder(); for ( int j = 0; j < columns.length; j++ ) { final String column = columns[j]; diff --git a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java new file mode 100644 index 0000000000..25f482a102 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java @@ -0,0 +1,39 @@ +package org.hibernate.test.component.basic2; + + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +/** + * {@inheritDoc} + * + * @author Mathieu Grenonville + */ +@Entity +public class Component { + + @Id + private Long id; + @Embedded + private Component.Emb emb; + + public Component() { + } + + @Access(AccessType.FIELD) + @Embeddable + public static class Emb { + + @OneToMany(targetEntity = Stuff.class) + Set stuffs = new HashSet(); + + @Entity + public static class Stuff { + @Id + private Long id; + } + } + + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java index c1010be4fb..a067ca8f44 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java @@ -23,6 +23,7 @@ */ package org.hibernate.test.component.basic2; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import org.hibernate.Session; @@ -36,7 +37,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; public class ComponentJoinsTest extends BaseCoreFunctionalTestCase { @Override public Class[] getAnnotatedClasses() { - return new Class[] { Person.class }; + return new Class[]{Person.class, Component.class, Component.Emb.Stuff.class}; } @Test @@ -45,14 +46,27 @@ public class ComponentJoinsTest extends BaseCoreFunctionalTestCase { Session session = openSession(); session.beginTransaction(); // use it in WHERE - session.createQuery( "select p from Person p join p.name as n where n.lastName like '%'" ).list(); + session.createQuery("select p from Person p join p.name as n where n.lastName like '%'").list(); // use it in SELECT - session.createQuery( "select n.lastName from Person p join p.name as n" ).list(); - session.createQuery( "select n from Person p join p.name as n" ).list(); + session.createQuery("select n.lastName from Person p join p.name as n").list(); + session.createQuery("select n from Person p join p.name as n").list(); // use it in ORDER BY - session.createQuery( "select n from Person p join p.name as n order by n.lastName" ).list(); - session.createQuery( "select n from Person p join p.name as n order by p" ).list(); - session.createQuery( "select n from Person p join p.name as n order by n" ).list(); + session.createQuery("select n from Person p join p.name as n order by n.lastName").list(); + session.createQuery("select n from Person p join p.name as n order by p").list(); + session.createQuery("select n from Person p join p.name as n order by n").list(); + session.getTransaction().commit(); + session.close(); + } + + @Test + @TestForIssue(jiraKey = "HHH-7849") + public void testComponentJoins_HHH_7849() { + // Just checking proper query construction and syntax checking via database query parser... + Session session = openSession(); + session.beginTransaction(); + // use it in WHERE + session.createQuery("select c from Component c join c.emb as e where e.stuffs is empty ").list(); + session.getTransaction().commit(); session.close(); } From c75b045adc3fc4ed908b2ed133646198d9aad5f7 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Mon, 10 Dec 2012 14:15:53 -0500 Subject: [PATCH 30/58] HHH-7849 Formatting --- .../test/component/basic2/Component.java | 4 +--- .../component/basic2/ComponentJoinsTest.java | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java index 25f482a102..f70f1766db 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java +++ b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/Component.java @@ -15,12 +15,10 @@ public class Component { @Id private Long id; + @Embedded private Component.Emb emb; - public Component() { - } - @Access(AccessType.FIELD) @Embeddable public static class Emb { diff --git a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java index a067ca8f44..fdf1ee9281 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/component/basic2/ComponentJoinsTest.java @@ -37,7 +37,10 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; public class ComponentJoinsTest extends BaseCoreFunctionalTestCase { @Override public Class[] getAnnotatedClasses() { - return new Class[]{Person.class, Component.class, Component.Emb.Stuff.class}; + return new Class[] { + Person.class, + Component.class, + Component.Emb.Stuff.class }; } @Test @@ -46,26 +49,26 @@ public class ComponentJoinsTest extends BaseCoreFunctionalTestCase { Session session = openSession(); session.beginTransaction(); // use it in WHERE - session.createQuery("select p from Person p join p.name as n where n.lastName like '%'").list(); + session.createQuery( "select p from Person p join p.name as n where n.lastName like '%'" ).list(); // use it in SELECT - session.createQuery("select n.lastName from Person p join p.name as n").list(); - session.createQuery("select n from Person p join p.name as n").list(); + session.createQuery( "select n.lastName from Person p join p.name as n" ).list(); + session.createQuery( "select n from Person p join p.name as n" ).list(); // use it in ORDER BY - session.createQuery("select n from Person p join p.name as n order by n.lastName").list(); - session.createQuery("select n from Person p join p.name as n order by p").list(); - session.createQuery("select n from Person p join p.name as n order by n").list(); + session.createQuery( "select n from Person p join p.name as n order by n.lastName" ).list(); + session.createQuery( "select n from Person p join p.name as n order by p" ).list(); + session.createQuery( "select n from Person p join p.name as n order by n" ).list(); session.getTransaction().commit(); session.close(); } @Test @TestForIssue(jiraKey = "HHH-7849") - public void testComponentJoins_HHH_7849() { + public void testComponentJoinsHHH7849() { // Just checking proper query construction and syntax checking via database query parser... Session session = openSession(); session.beginTransaction(); // use it in WHERE - session.createQuery("select c from Component c join c.emb as e where e.stuffs is empty ").list(); + session.createQuery( "select c from Component c join c.emb as e where e.stuffs is empty " ).list(); session.getTransaction().commit(); session.close(); From 598c6d3d25a5f2095ba18000509466c9e25343bd Mon Sep 17 00:00:00 2001 From: brmeyer Date: Mon, 10 Dec 2012 14:16:20 -0500 Subject: [PATCH 31/58] HHH-7849 Cleaner fix. Added additional regression test. --- .../hql/internal/ast/tree/ComponentJoin.java | 3 +-- .../hibernate/hql/internal/ast/tree/IdentNode.java | 5 +++-- .../criteria/components/ComponentCriteriaTest.java | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java index 29099a5fe9..cb152908dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/ComponentJoin.java @@ -57,8 +57,7 @@ public class ComponentJoin extends FromElement { fromClause.addJoinByPathMap( componentPath, this ); initializeComponentJoin( new ComponentFromElementType( this ) ); - String[] cols = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty ); - this.columns = cols.length == 0 ? new String[] { null} : cols; + this.columns = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty ); StringBuilder buf = new StringBuilder(); for ( int j = 0; j < columns.length; j++ ) { final String column = columns[j]; diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java index 28188cbaba..ff79488f61 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/IdentNode.java @@ -174,11 +174,12 @@ public class IdentNode extends FromReferenceNode implements SelectExpression { } setText( joinedFragment ); } + return true; } - else { + else if ( columnExpressions.length > 0 ) { setText( columnExpressions[0] ); + return true; } - return true; } return false; } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java index a93a4a8d8a..791ce87ad7 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/components/ComponentCriteriaTest.java @@ -25,6 +25,7 @@ package org.hibernate.jpa.test.criteria.components; import java.util.List; import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; @@ -64,6 +65,17 @@ public class ComponentCriteriaTest extends BaseEntityManagerFunctionalTestCase { Assert.assertEquals( 1, list.size() ); em.getTransaction().commit(); em.close(); + + // HHH-5792 + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + TypedQuery< Client > q = em.createQuery( + "SELECT c FROM Client c JOIN c.name n WHERE n.firstName = '" + + client.getName().getFirstName() + "'", + Client.class ); + Assert.assertEquals( 1, q.getResultList().size() ); + em.getTransaction().commit(); + em.close(); em = getOrCreateEntityManager(); em.getTransaction().begin(); From 8463057b85db928fe8d3aa0b4379bc5b0f255c71 Mon Sep 17 00:00:00 2001 From: Scott Marlow Date: Mon, 10 Dec 2012 18:43:55 -0500 Subject: [PATCH 32/58] HHH-7850 BulkAccessorFactory Java 7 verify error resolved by JASSIST-163 BulkAccessorFactory.java.diff2 patch from Shigeru Chiba. --- .../javassist/BulkAccessorFactory.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/BulkAccessorFactory.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/BulkAccessorFactory.java index 6e4fa62465..40be75c89a 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/BulkAccessorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/BulkAccessorFactory.java @@ -31,9 +31,11 @@ import javassist.CannotCompileException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Bytecode; import javassist.bytecode.ClassFile; +import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; +import javassist.bytecode.StackMapTable; import javassist.util.proxy.FactoryHelper; import javassist.util.proxy.RuntimeSupport; @@ -244,6 +246,7 @@ class BulkAccessorFactory { MethodInfo mi = new MethodInfo( cp, GENERATED_SETTER_NAME, desc ); Bytecode code = new Bytecode( cp, 4, 6 ); + StackMapTable stackmap = null; /* | this | bean | args | i | raw bean | exception | */ if ( setters.length > 0 ) { int start, end; // required to exception table @@ -321,7 +324,8 @@ class BulkAccessorFactory { /* current stack len = 0 */ // register in exception table int throwableType_index = cp.addClassInfo( THROWABLE_CLASS_NAME ); - code.addExceptionHandler( start, end, code.currentPc(), throwableType_index ); + int handler_pc = code.currentPc(); + code.addExceptionHandler( start, end, handler_pc, throwableType_index ); // astore 5 // store exception code.addAstore( 5 ); // new // BulkAccessorException @@ -337,13 +341,24 @@ class BulkAccessorFactory { code.addInvokespecial( BULKEXCEPTION_CLASS_NAME, MethodInfo.nameInit, cons_desc ); // athrow code.addOpcode( Opcode.ATHROW ); + StackMapTable.Writer writer = new StackMapTable.Writer(32); + int[] localTags = { StackMapTable.OBJECT, StackMapTable.OBJECT, StackMapTable.OBJECT, StackMapTable.INTEGER }; + int[] localData = { cp.getThisClassInfo(), cp.addClassInfo("java/lang/Object"), + cp.addClassInfo("[Ljava/lang/Object;"), 0}; + int[] stackTags = { StackMapTable.OBJECT }; + int[] stackData = { throwableType_index }; + writer.fullFrame(handler_pc, localTags, localData, stackTags, stackData); + stackmap = writer.toStackMapTable(cp); } else { // return code.addOpcode( Opcode.RETURN ); } - - mi.setCodeAttribute( code.toCodeAttribute() ); + CodeAttribute ca = code.toCodeAttribute(); + if (stackmap != null) { + ca.setAttribute(stackmap); + } + mi.setCodeAttribute( ca ); mi.setAccessFlags( AccessFlag.PUBLIC ); classfile.addMethod( mi ); } From 58978a486f6d56697274c8a358e70b8925be0a62 Mon Sep 17 00:00:00 2001 From: Dustin Schultz Date: Mon, 3 Dec 2012 15:35:09 -0700 Subject: [PATCH 33/58] HHH-7747 - Fixes CNFE regression for runtime field level class enhancing. Also removes the usage of ClassPool.getDefault() and creates a new ClassPool on each usage. HHH-7747 - Add the entity class to the ClassPool to support modular classloading like OSGI. Remove unused import in FieldTransformer. HHH-7747 - Enhanced test to ensure that class enhancement sees all classes of an entity. Added test to ensure that StackMapTables are non-null for Javassist added methods. --- .../internal/javassist/FieldTransformer.java | 49 +++++++++----- .../javassist/JavassistClassTransformer.java | 19 +++++- .../ejb/test/instrument/SimpleRelation.java | 21 ++++++ .../instrument/InstrumentedClassLoader.java | 26 +++++++- ...nterceptFieldClassFileTransformerTest.java | 65 ++++++++++++++++--- .../hibernate/jpa/test/instrument/Simple.java | 19 ++++++ 6 files changed, 169 insertions(+), 30 deletions(-) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java index 2e547c3627..8c8732fc1b 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java @@ -32,6 +32,7 @@ import java.util.Iterator; import java.util.List; import javassist.CannotCompileException; +import javassist.ClassPool; import javassist.bytecode.AccessFlag; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; @@ -43,6 +44,8 @@ import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; +import javassist.bytecode.StackMapTable; +import javassist.bytecode.stackmap.MapMaker; /** * The thing that handles actual class enhancement in regards to @@ -50,6 +53,7 @@ import javassist.bytecode.Opcode; * * @author Muga Nishizawa * @author Steve Ebersole + * @author Dustin Schultz */ public class FieldTransformer { @@ -79,13 +83,20 @@ public class FieldTransformer { + HANDLER_FIELD_DESCRIPTOR + ")V"; private FieldFilter filter; + + private ClassPool classPool; public FieldTransformer() { - this(null); + this(null, null); } - public FieldTransformer(FieldFilter f) { + public FieldTransformer(FieldFilter f, ClassPool c) { filter = f; + classPool = c; + } + + public void setClassPool(ClassPool c) { + classPool = c; } public void setFieldFilter(FieldFilter f) { @@ -130,7 +141,7 @@ public class FieldTransformer { } private void addGetFieldHandlerMethod(ClassFile classfile) - throws CannotCompileException { + throws CannotCompileException, BadBytecode { ConstPool cp = classfile.getConstPool(); int this_class_index = cp.getThisClassInfo(); MethodInfo minfo = new MethodInfo(cp, GETFIELDHANDLER_METHOD_NAME, @@ -148,11 +159,13 @@ public class FieldTransformer { code.addOpcode(Opcode.ARETURN); minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); + StackMapTable smt = MapMaker.make(classPool, minfo); + minfo.getCodeAttribute().setAttribute(smt); classfile.addMethod(minfo); } private void addSetFieldHandlerMethod(ClassFile classfile) - throws CannotCompileException { + throws CannotCompileException, BadBytecode { ConstPool cp = classfile.getConstPool(); int this_class_index = cp.getThisClassInfo(); MethodInfo minfo = new MethodInfo(cp, SETFIELDHANDLER_METHOD_NAME, @@ -172,6 +185,8 @@ public class FieldTransformer { code.addOpcode(Opcode.RETURN); minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); + StackMapTable smt = MapMaker.make(classPool, minfo); + minfo.getCodeAttribute().setAttribute(smt); classfile.addMethod(minfo); } @@ -185,7 +200,7 @@ public class FieldTransformer { } private void addReadWriteMethods(ClassFile classfile) - throws CannotCompileException { + throws CannotCompileException, BadBytecode { List fields = classfile.getFields(); for (Iterator field_iter = fields.iterator(); field_iter.hasNext();) { FieldInfo finfo = (FieldInfo) field_iter.next(); @@ -205,7 +220,7 @@ public class FieldTransformer { } private void addReadMethod(ClassFile classfile, FieldInfo finfo) - throws CannotCompileException { + throws CannotCompileException, BadBytecode { ConstPool cp = classfile.getConstPool(); int this_class_index = cp.getThisClassInfo(); String desc = "()" + finfo.getDescriptor(); @@ -254,11 +269,13 @@ public class FieldTransformer { minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); + StackMapTable smt = MapMaker.make(classPool, minfo); + minfo.getCodeAttribute().setAttribute(smt); classfile.addMethod(minfo); } private void addWriteMethod(ClassFile classfile, FieldInfo finfo) - throws CannotCompileException { + throws CannotCompileException, BadBytecode { ConstPool cp = classfile.getConstPool(); int this_class_index = cp.getThisClassInfo(); String desc = "(" + finfo.getDescriptor() + ")V"; @@ -320,11 +337,13 @@ public class FieldTransformer { minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); + StackMapTable smt = MapMaker.make(classPool, minfo); + minfo.getCodeAttribute().setAttribute(smt); classfile.addMethod(minfo); } private void transformInvokevirtualsIntoPutAndGetfields(ClassFile classfile) - throws CannotCompileException { + throws CannotCompileException, BadBytecode { List methods = classfile.getMethods(); for (Iterator method_iter = methods.iterator(); method_iter.hasNext();) { MethodInfo minfo = (MethodInfo) method_iter.next(); @@ -341,15 +360,13 @@ public class FieldTransformer { } 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 ); - } - + int pos = iter.next(); + pos = transformInvokevirtualsIntoGetfields(classfile, iter, pos); + pos = transformInvokevirtualsIntoPutfields(classfile, iter, pos); } + + StackMapTable smt = MapMaker.make(classPool, minfo); + minfo.getCodeAttribute().setAttribute(smt); } } diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/JavassistClassTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/JavassistClassTransformer.java index 91d6a1285a..d8f440117e 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/JavassistClassTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/JavassistClassTransformer.java @@ -30,6 +30,8 @@ import java.io.DataOutputStream; import java.io.IOException; import java.security.ProtectionDomain; +import javassist.ClassClassPath; +import javassist.ClassPool; import javassist.bytecode.ClassFile; import org.jboss.logging.Logger; @@ -44,6 +46,7 @@ import org.hibernate.internal.CoreMessageLogger; * * @author Emmanuel Bernard * @author Steve Ebersole + * @author Dustin Schultz */ public class JavassistClassTransformer extends AbstractClassTransformerImpl { @@ -70,7 +73,17 @@ public class JavassistClassTransformer extends AbstractClassTransformerImpl { LOG.unableToBuildEnhancementMetamodel( className ); return classfileBuffer; } - FieldTransformer transformer = getFieldTransformer( classfile ); + // This is the same as ClassPool.getDefault() but ensures a new ClassPool per + ClassPool cp = new ClassPool(); + cp.appendSystemPath(); + cp.appendClassPath(new ClassClassPath(this.getClass())); + cp.appendClassPath(new ClassClassPath(classfile.getClass())); + try { + cp.makeClassIfNew(new ByteArrayInputStream(classfileBuffer)); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + FieldTransformer transformer = getFieldTransformer( classfile, cp ); if ( transformer != null ) { LOG.debugf( "Enhancing %s", className ); DataOutputStream out = null; @@ -97,7 +110,7 @@ public class JavassistClassTransformer extends AbstractClassTransformerImpl { return classfileBuffer; } - protected FieldTransformer getFieldTransformer(final ClassFile classfile) { + protected FieldTransformer getFieldTransformer(final ClassFile classfile, final ClassPool classPool) { if ( alreadyInstrumented( classfile ) ) { return null; } @@ -118,7 +131,7 @@ public class JavassistClassTransformer extends AbstractClassTransformerImpl { public boolean handleWriteAccess(String fieldOwnerClassName, String fieldName) { return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName ); } - } + }, classPool ); } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java new file mode 100644 index 0000000000..0385c97a49 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java @@ -0,0 +1,21 @@ +package org.hibernate.ejb.test.instrument; + +/** + * A simple entity relation used by {@link Simple} to ensure that enhanced + * classes load all classes. + * + * @author Dustin Schultz + */ +public class SimpleRelation { + + private String blah; + + public String getBlah() { + return blah; + } + + public void setBlah(String blah) { + this.blah = blah; + } + +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InstrumentedClassLoader.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InstrumentedClassLoader.java index 54a504c666..ce729dceb6 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InstrumentedClassLoader.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InstrumentedClassLoader.java @@ -10,6 +10,7 @@ import org.hibernate.jpa.internal.instrument.InterceptFieldClassFileTransformer; /** * @author Emmanuel Bernard + * @author Dustin Schultz */ public class InstrumentedClassLoader extends ClassLoader { private List entities; @@ -20,9 +21,28 @@ public class InstrumentedClassLoader extends ClassLoader { @Override public Class loadClass(String name) throws ClassNotFoundException { - if ( name != null && name.startsWith( "java.lang." ) ) return getParent().loadClass( name ); + // Do not instrument the following packages + if (name != null + && (name.startsWith("java.lang.") || + name.startsWith("java.util."))) + return getParent().loadClass(name); Class c = findLoadedClass( name ); if ( c != null ) return c; + + byte[] transformed = loadClassBytes(name); + + return defineClass( name, transformed, 0, transformed.length ); + } + + /** + * Specialized {@link ClassLoader#loadClass(String)} that returns the class + * as a byte array. + * + * @param name + * @return + * @throws ClassNotFoundException + */ + public byte[] loadClassBytes(String name) throws ClassNotFoundException { InputStream is = this.getResourceAsStream( name.replace( ".", "/" ) + ".class" ); if ( is == null ) throw new ClassNotFoundException( name ); byte[] buffer = new byte[409600]; @@ -66,8 +86,8 @@ public class InstrumentedClassLoader extends ClassLoader { catch (IllegalClassFormatException e) { throw new ClassNotFoundException( name + " not found", e ); } - - return defineClass( name, transformed, 0, transformed.length ); + + return transformed; } public void setEntities(List entities) { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java index ecd74c73ff..57149f5196 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java @@ -1,18 +1,42 @@ //$Id$ package org.hibernate.jpa.test.instrument; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.StackMapTable; + +import org.hibernate.testing.TestForIssue; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** * @author Emmanuel Bernard * @author Hardy Ferentschik + * @author Dustin Schultz */ public class InterceptFieldClassFileTransformerTest { + + private List entities = new ArrayList(); + private InstrumentedClassLoader loader = null; + + @Before + public void setup() { + entities.add( "org.hibernate.ejb.test.instrument.Simple" ); + // use custom class loader which enhances the class + InstrumentedClassLoader cl = new InstrumentedClassLoader( Thread.currentThread().getContextClassLoader() ); + cl.setEntities( entities ); + this.loader = cl; + } + /** * Tests that class file enhancement works. * @@ -20,9 +44,6 @@ public class InterceptFieldClassFileTransformerTest { */ @Test public void testEnhancement() throws Exception { - List entities = new ArrayList(); - entities.add( "org.hibernate.jpa.test.instrument.Simple" ); - // sanity check that the class is unmodified and does not contain getFieldHandler() try { Simple.class.getDeclaredMethod( "getFieldHandler" ); @@ -30,15 +51,43 @@ public class InterceptFieldClassFileTransformerTest { } catch ( NoSuchMethodException nsme ) { // success } - - // use custom class loader which enhances the class - InstrumentedClassLoader cl = new InstrumentedClassLoader( Thread.currentThread().getContextClassLoader() ); - cl.setEntities( entities ); - Class clazz = cl.loadClass( entities.get( 0 ) ); + + Class clazz = loader.loadClass( entities.get( 0 ) ); // javassist is our default byte code enhancer. Enhancing will eg add the method getFieldHandler() // see org.hibernate.bytecode.internal.javassist.FieldTransformer Method method = clazz.getDeclaredMethod( "getFieldHandler" ); Assert.assertNotNull( method ); } + + /** + * Tests that methods that were enhanced by javassist have + * StackMapTables for java verification. Without these, + * java.lang.VerifyError's occur in JDK7. + * + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IOException + */ + @Test + @TestForIssue(jiraKey = "HHH-7747") + public void testStackMapTableEnhancment() throws ClassNotFoundException, + InstantiationException, IllegalAccessException, IOException { + byte[] classBytes = loader.loadClassBytes(entities.get(0)); + ClassPool classPool = new ClassPool(); + CtClass ctClass = classPool.makeClass(new ByteArrayInputStream( + classBytes)); + for (CtMethod ctMethod : ctClass.getMethods()) { + //Only check methods that were added by javassist + if (ctMethod.getName().startsWith("$javassist_")) { + AttributeInfo attributeInfo = ctMethod + .getMethodInfo().getCodeAttribute() + .getAttribute(StackMapTable.tag); + Assert.assertNotNull(attributeInfo); + StackMapTable smt = (StackMapTable)attributeInfo; + Assert.assertNotNull(smt.get()); + } + } + } } diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java index 598b19d609..3056e7e605 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java @@ -1,12 +1,23 @@ //$Id$ package org.hibernate.jpa.test.instrument; +import java.util.Collection; + +import org.hibernate.ejb.instrument.InterceptFieldClassFileTransformer; + /** + * A simple entity to be enhanced by the {@link InterceptFieldClassFileTransformer} + * * @author Emmanuel Bernard + * @author Dustin Schultz */ public class Simple { private String name; + + // Have an additional attribute that will ensure that the enhanced classes + // will see all class attributes of an entity without CNFEs + private Collection relations; public String getName() { return name; @@ -15,4 +26,12 @@ public class Simple { public void setName(String name) { this.name = name; } + + public Collection getRelations() { + return relations; + } + + public void setRelations(Collection relations) { + this.relations = relations; + } } From d26730fb3f7f4db6814a27a1cece03d28933bfe7 Mon Sep 17 00:00:00 2001 From: Scott Marlow Date: Tue, 11 Dec 2012 16:31:14 -0500 Subject: [PATCH 34/58] HHH-7850 minor test package changes --- .../test/instrument/InterceptFieldClassFileTransformerTest.java | 2 +- .../src/test/java/org/hibernate/jpa/test/instrument/Simple.java | 2 +- .../hibernate/{ejb => jpa}/test/instrument/SimpleRelation.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename hibernate-entitymanager/src/test/java/org/hibernate/{ejb => jpa}/test/instrument/SimpleRelation.java (87%) diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java index 57149f5196..afa1a09638 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/InterceptFieldClassFileTransformerTest.java @@ -30,7 +30,7 @@ public class InterceptFieldClassFileTransformerTest { @Before public void setup() { - entities.add( "org.hibernate.ejb.test.instrument.Simple" ); + entities.add( "org.hibernate.jpa.test.instrument.Simple" ); // use custom class loader which enhances the class InstrumentedClassLoader cl = new InstrumentedClassLoader( Thread.currentThread().getContextClassLoader() ); cl.setEntities( entities ); diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java index 3056e7e605..adeef1f054 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/Simple.java @@ -3,7 +3,7 @@ package org.hibernate.jpa.test.instrument; import java.util.Collection; -import org.hibernate.ejb.instrument.InterceptFieldClassFileTransformer; +import org.hibernate.jpa.internal.instrument.InterceptFieldClassFileTransformer; /** diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/SimpleRelation.java similarity index 87% rename from hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/SimpleRelation.java index 0385c97a49..734bb9a857 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/instrument/SimpleRelation.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/instrument/SimpleRelation.java @@ -1,4 +1,4 @@ -package org.hibernate.ejb.test.instrument; +package org.hibernate.jpa.test.instrument; /** * A simple entity relation used by {@link Simple} to ensure that enhanced From b4e78a99e579114c5af6d1af1e62ab4f7fdb88d0 Mon Sep 17 00:00:00 2001 From: Scott Marlow Date: Tue, 11 Dec 2012 14:53:36 -0500 Subject: [PATCH 35/58] HHH-7747 check if the code attribute is null when building the StackMapTable --- .../internal/javassist/FieldTransformer.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java index 8c8732fc1b..eb586fde0c 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/javassist/FieldTransformer.java @@ -159,8 +159,11 @@ public class FieldTransformer { code.addOpcode(Opcode.ARETURN); minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); - StackMapTable smt = MapMaker.make(classPool, minfo); - minfo.getCodeAttribute().setAttribute(smt); + CodeAttribute codeAttribute = minfo.getCodeAttribute(); + if (codeAttribute != null) { + StackMapTable smt = MapMaker.make(classPool, minfo); + codeAttribute.setAttribute(smt); + } classfile.addMethod(minfo); } @@ -185,8 +188,11 @@ public class FieldTransformer { code.addOpcode(Opcode.RETURN); minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); - StackMapTable smt = MapMaker.make(classPool, minfo); - minfo.getCodeAttribute().setAttribute(smt); + CodeAttribute codeAttribute = minfo.getCodeAttribute(); + if (codeAttribute != null) { + StackMapTable smt = MapMaker.make(classPool, minfo); + codeAttribute.setAttribute(smt); + } classfile.addMethod(minfo); } @@ -269,8 +275,11 @@ public class FieldTransformer { minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); - StackMapTable smt = MapMaker.make(classPool, minfo); - minfo.getCodeAttribute().setAttribute(smt); + CodeAttribute codeAttribute = minfo.getCodeAttribute(); + if (codeAttribute != null) { + StackMapTable smt = MapMaker.make(classPool, minfo); + codeAttribute.setAttribute(smt); + } classfile.addMethod(minfo); } @@ -337,8 +346,11 @@ public class FieldTransformer { minfo.setCodeAttribute(code.toCodeAttribute()); minfo.setAccessFlags(AccessFlag.PUBLIC); - StackMapTable smt = MapMaker.make(classPool, minfo); - minfo.getCodeAttribute().setAttribute(smt); + CodeAttribute codeAttribute = minfo.getCodeAttribute(); + if (codeAttribute != null) { + StackMapTable smt = MapMaker.make(classPool, minfo); + codeAttribute.setAttribute(smt); + } classfile.addMethod(minfo); } @@ -364,9 +376,8 @@ public class FieldTransformer { pos = transformInvokevirtualsIntoGetfields(classfile, iter, pos); pos = transformInvokevirtualsIntoPutfields(classfile, iter, pos); } - StackMapTable smt = MapMaker.make(classPool, minfo); - minfo.getCodeAttribute().setAttribute(smt); + codeAttr.setAttribute(smt); } } From c8aa47ba22218ba38360b27dca3e8c7cdd813e59 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Tue, 11 Dec 2012 18:03:06 -0500 Subject: [PATCH 36/58] HHH-7435 Performance bottleneck on Javassist --- libraries.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries.gradle b/libraries.gradle index 54e99697a2..f04f9eabe1 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -51,7 +51,7 @@ ext { dom4j: 'dom4j:dom4j:1.6.1@jar', // Javassist - javassist: 'org.javassist:javassist:3.15.0-GA', + javassist: 'org.javassist:javassist:3.17.1-GA', // javax jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-7plus', From 669ec5002f475bf1cb8ff00dad812112a0b3fe41 Mon Sep 17 00:00:00 2001 From: Kamyar Sajjadi Date: Wed, 12 Dec 2012 10:47:31 +0100 Subject: [PATCH 37/58] HHH-7029 - prependListeners changed to appendListeners in chapter 7.9.1. Integrator use-cases example --- .../services/extras/register-event-listeners-example.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/services/extras/register-event-listeners-example.java b/documentation/src/main/docbook/devguide/en-US/chapters/services/extras/register-event-listeners-example.java index 62d5f9b271..7231d09939 100644 --- a/documentation/src/main/docbook/devguide/en-US/chapters/services/extras/register-event-listeners-example.java +++ b/documentation/src/main/docbook/devguide/en-US/chapters/services/extras/register-event-listeners-example.java @@ -18,6 +18,6 @@ public class MyIntegrator implements org.hibernate.integrator.spi.Integrator { // 2) This form adds the specified listener(s) to the beginning of the listener chain eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledFirst ); // 3) This form adds the specified listener(s) to the end of the listener chain - eventListenerRegistry.prependListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast ); + eventListenerRegistry.appendListeners( EventType.AUTO_FLUSH, myListenersToBeCalledLast ); } -} \ No newline at end of file +} From c0011f1cc60504123a3856516a0a4f6e19cdf63f Mon Sep 17 00:00:00 2001 From: Kamyar Sajjadi Date: Wed, 12 Dec 2012 14:27:55 +0100 Subject: [PATCH 38/58] HHH-5973 - Replaced , with + in AuditInterceptor.java and in events.xml --- .../en-US/chapters/events/extras/AuditInterceptor.java | 4 ++-- .../src/main/docbook/manual/en-US/content/events.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/events/extras/AuditInterceptor.java b/documentation/src/main/docbook/devguide/en-US/chapters/events/extras/AuditInterceptor.java index b5801321fa..76e8b5bde2 100644 --- a/documentation/src/main/docbook/devguide/en-US/chapters/events/extras/AuditInterceptor.java +++ b/documentation/src/main/docbook/devguide/en-US/chapters/events/extras/AuditInterceptor.java @@ -69,11 +69,11 @@ public class AuditInterceptor extends EmptyInterceptor { public void afterTransactionCompletion(Transaction tx) { if ( tx.wasCommitted() ) { - System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads); + System.out.println("Creations: " + creates + ", Updates: " + updates + "Loads: " + loads); } updates=0; creates=0; loads=0; } -} \ No newline at end of file +} diff --git a/documentation/src/main/docbook/manual/en-US/content/events.xml b/documentation/src/main/docbook/manual/en-US/content/events.xml index 05d8304153..2a17246dba 100755 --- a/documentation/src/main/docbook/manual/en-US/content/events.xml +++ b/documentation/src/main/docbook/manual/en-US/content/events.xml @@ -102,7 +102,7 @@ public class AuditInterceptor extends EmptyInterceptor { public void afterTransactionCompletion(Transaction tx) { if ( tx.wasCommitted() ) { - System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads); + System.out.println("Creations: " + creates + ", Updates: " + updates + "Loads: " + loads); } updates=0; creates=0; From 234c4915239589219a3896522d246b8f001ee074 Mon Sep 17 00:00:00 2001 From: Kamyar Sajjadi Date: Wed, 12 Dec 2012 16:37:46 +0100 Subject: [PATCH 39/58] HHH-4562 - Fixed the typo in chapter 7.3.1. Added bigint after personId --- .../main/docbook/manual/en-US/content/association_mapping.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/main/docbook/manual/en-US/content/association_mapping.xml b/documentation/src/main/docbook/manual/en-US/content/association_mapping.xml index 72b8df541b..d02fb874c3 100755 --- a/documentation/src/main/docbook/manual/en-US/content/association_mapping.xml +++ b/documentation/src/main/docbook/manual/en-US/content/association_mapping.xml @@ -184,7 +184,7 @@ create table Address ( addressId bigint not null primary key, personId bigint no ]]> From dc3283f50a147f458bc2ca827530ff8455df325e Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 12 Dec 2012 16:16:31 -0500 Subject: [PATCH 40/58] HHH-7856 Deprecate TableHiLoGenerator and TableGenerator --- .../org/hibernate/id/SequenceGenerator.java | 1 - .../hibernate/id/SequenceHiLoGenerator.java | 4 - .../java/org/hibernate/id/TableGenerator.java | 4 + .../org/hibernate/id/TableHiLoGenerator.java | 4 + .../hibernate/id/TableHiLoGeneratorTest.java | 179 ------------------ 5 files changed, 8 insertions(+), 184 deletions(-) delete mode 100644 hibernate-core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/id/SequenceGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/SequenceGenerator.java index 52d508daa9..5eaef363f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/SequenceGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/SequenceGenerator.java @@ -49,7 +49,6 @@ import org.hibernate.type.Type; * Mapping parameters supported: sequence, parameters. * * @see SequenceHiLoGenerator - * @see TableHiLoGenerator * @author Gavin King */ public class SequenceGenerator diff --git a/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java index b2742db804..da02f952e0 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java @@ -40,12 +40,8 @@ import org.hibernate.type.Type; * oracle-style sequence that generates hi values. The user may specify a * maximum lo value to determine how often new hi values are fetched.
*
- * If sequences are not available, TableHiLoGenerator might be an - * alternative.
- *
* Mapping parameters supported: sequence, max_lo, parameters. * - * @see TableHiLoGenerator * @author Gavin King */ public class SequenceHiLoGenerator extends SequenceGenerator { diff --git a/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java index 06469597a2..496697ac71 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java @@ -41,6 +41,7 @@ import org.hibernate.engine.jdbc.internal.FormatStyle; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jdbc.AbstractReturningWork; @@ -71,7 +72,10 @@ import org.hibernate.type.Type; * * @see TableHiLoGenerator * @author Gavin King + * + * @deprecate use {@link SequenceStyleGenerator} instead. */ +@Deprecated public class TableGenerator implements PersistentIdentifierGenerator, Configurable { /* COLUMN and TABLE should be renamed but it would break the public API */ /** The column parameter */ diff --git a/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java index be450cbee6..0fe513be23 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/TableHiLoGenerator.java @@ -29,6 +29,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.enhanced.AccessCallback; import org.hibernate.id.enhanced.OptimizerFactory; +import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.type.Type; @@ -47,7 +48,10 @@ import org.hibernate.type.Type; * * @see SequenceHiLoGenerator * @author Gavin King + * + * @deprecate use {@link SequenceStyleGenerator} instead. */ +@Deprecated public class TableHiLoGenerator extends TableGenerator { /** * The max_lo parameter diff --git a/hibernate-core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java deleted file mode 100644 index a1bef70227..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2010, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.id; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Properties; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import org.hibernate.Session; -import org.hibernate.TestingDatabaseInfo; -import org.hibernate.cfg.Configuration; -import org.hibernate.cfg.Environment; -import org.hibernate.cfg.NamingStrategy; -import org.hibernate.cfg.ObjectNameNormalizer; -import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.internal.SessionImpl; -import org.hibernate.jdbc.Work; -import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.testing.ServiceRegistryBuilder; -import org.hibernate.testing.junit4.BaseUnitTestCase; -import org.hibernate.type.StandardBasicTypes; - -import static org.junit.Assert.assertEquals; - -/** - * I went back to 3.3 source and grabbed the code/logic as it existed back then and crafted this - * unit test so that we can make sure the value keep being generated in the expected manner - * - * @author Steve Ebersole - */ -@SuppressWarnings({ "deprecation" }) -public class TableHiLoGeneratorTest extends BaseUnitTestCase { - private static final String GEN_TABLE = "generator_table"; - private static final String GEN_COLUMN = TableHiLoGenerator.DEFAULT_COLUMN_NAME; - - private Configuration cfg; - private ServiceRegistry serviceRegistry; - private SessionFactoryImplementor sessionFactory; - private TableHiLoGenerator generator; - - @Before - public void setUp() throws Exception { - Properties properties = new Properties(); - properties.setProperty( TableGenerator.TABLE, GEN_TABLE ); - properties.setProperty( TableGenerator.COLUMN, GEN_COLUMN ); - properties.setProperty( TableHiLoGenerator.MAX_LO, "3" ); - properties.put( - PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, - new ObjectNameNormalizer() { - @Override - protected boolean isUseQuotedIdentifiersGlobally() { - return false; - } - - @Override - protected NamingStrategy getNamingStrategy() { - return cfg.getNamingStrategy(); - } - } - ); - - Dialect dialect = new H2Dialect(); - - generator = new TableHiLoGenerator(); - generator.configure( StandardBasicTypes.LONG, properties, dialect ); - - cfg = TestingDatabaseInfo.buildBaseConfiguration() - .setProperty( Environment.HBM2DDL_AUTO, "create-drop" ); - cfg.addAuxiliaryDatabaseObject( - new SimpleAuxiliaryDatabaseObject( - generator.sqlCreateStrings( dialect )[0], - generator.sqlDropStrings( dialect )[0] - ) - ); - - cfg.addAuxiliaryDatabaseObject( - new SimpleAuxiliaryDatabaseObject( - generator.sqlCreateStrings( dialect )[1], - null - ) - ); - - serviceRegistry = ServiceRegistryBuilder.buildServiceRegistry( cfg.getProperties() ); - sessionFactory = (SessionFactoryImplementor) cfg.buildSessionFactory( serviceRegistry ); - } - - @After - public void tearDown() throws Exception { - if ( sessionFactory != null ) { - sessionFactory.close(); - } - if ( serviceRegistry != null ) { - ServiceRegistryBuilder.destroy( serviceRegistry ); - } - } - - @Test - public void testHiLoAlgorithm() { - SessionImpl session = (SessionImpl) sessionFactory.openSession(); - session.beginTransaction(); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // initially sequence should be uninitialized - assertEquals( 0L, extractInDatabaseValue( session ) ); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Long generatedValue = (Long) generator.generate( session, null ); - assertEquals( 1L, generatedValue.longValue() ); - assertEquals( 1L, extractInDatabaseValue( session ) ); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - generatedValue = (Long) generator.generate( session, null ); - assertEquals( 2L, generatedValue.longValue() ); - assertEquals( 1L, extractInDatabaseValue( session ) ); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - generatedValue = (Long) generator.generate( session, null ); - assertEquals( 3L, generatedValue.longValue() ); - assertEquals( 1L, extractInDatabaseValue( session ) ); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - generatedValue = (Long) generator.generate( session, null ); - assertEquals( 4L, generatedValue.longValue() ); - assertEquals( 2L, extractInDatabaseValue( session ) ); - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - generatedValue = (Long) generator.generate( session, null ); - assertEquals( 5L, generatedValue.longValue() ); - assertEquals( 2L, extractInDatabaseValue( session ) ); - - session.getTransaction().commit(); - session.close(); - } - - private long extractInDatabaseValue(Session session) { - class WorkImpl implements Work { - private long value; - public void execute(Connection connection) throws SQLException { - PreparedStatement query = connection.prepareStatement( "select " + GEN_COLUMN + " from " + GEN_TABLE ); - ResultSet resultSet = query.executeQuery(); - resultSet.next(); - value = resultSet.getLong( 1 ); - } - } - WorkImpl work = new WorkImpl(); - session.doWork( work ); - return work.value; - } -} \ No newline at end of file From 2883cb85c37fbd6fd956f750fc8082aaa9be0fa1 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 12 Dec 2012 16:06:03 -0800 Subject: [PATCH 41/58] HHH-7860 : Log a warning when embed-xml attribute is used in mappings --- .../java/org/hibernate/cfg/HbmBinder.java | 27 ++++++++++++++++++- .../hibernate/internal/CoreMessageLogger.java | 8 ++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java index 6692db88f7..624552cacb 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java @@ -1404,6 +1404,12 @@ public final class HbmBinder { if ( nodeName == null ) nodeName = node.attributeValue( "name" ); collection.setNodeName( nodeName ); String embed = node.attributeValue( "embed-xml" ); + // sometimes embed is set to the default value when not specified in the mapping, + // so can't seem to determine if an attribute was explicitly set; + // log a warning if embed has a value different from the default. + if ( !StringHelper.isEmpty( embed ) && !"true".equals( embed ) ) { + LOG.embedXmlAttributesNoLongerSupported(); + } collection.setEmbedded( embed==null || "true".equals(embed) ); @@ -1627,6 +1633,12 @@ public final class HbmBinder { manyToOne.setReferencedEntityName( getEntityName( node, mappings ) ); String embed = node.attributeValue( "embed-xml" ); + // sometimes embed is set to the default value when not specified in the mapping, + // so can't seem to determine if an attribute was explicitly set; + // log a warning if embed has a value different from the default. + if ( !StringHelper.isEmpty( embed ) && !"true".equals( embed ) ) { + LOG.embedXmlAttributesNoLongerSupported(); + } manyToOne.setEmbedded( embed == null || "true".equals( embed ) ); String notFound = node.attributeValue( "not-found" ); @@ -1702,7 +1714,14 @@ public final class HbmBinder { initOuterJoinFetchSetting( node, oneToOne ); initLaziness( node, oneToOne, mappings, true ); - oneToOne.setEmbedded( "true".equals( node.attributeValue( "embed-xml" ) ) ); + String embed = node.attributeValue( "embed-xml" ); + // sometimes embed is set to the default value when not specified in the mapping, + // so can't seem to determine if an attribute was explicitly set; + // log a warning if embed has a value different from the default. + if ( !StringHelper.isEmpty( embed ) && !"true".equals( embed ) ) { + LOG.embedXmlAttributesNoLongerSupported(); + } + oneToOne.setEmbedded( "true".equals( embed ) ); Attribute fkNode = node.attribute( "foreign-key" ); if ( fkNode != null ) oneToOne.setForeignKeyName( fkNode.getValue() ); @@ -1730,6 +1749,12 @@ public final class HbmBinder { oneToMany.setReferencedEntityName( getEntityName( node, mappings ) ); String embed = node.attributeValue( "embed-xml" ); + // sometimes embed is set to the default value when not specified in the mapping, + // so can't seem to determine if an attribute was explicitly set; + // log a warning if embed has a value different from the default. + if ( !StringHelper.isEmpty( embed ) && !"true".equals( embed ) ) { + LOG.embedXmlAttributesNoLongerSupported(); + } oneToMany.setEmbedded( embed == null || "true".equals( embed ) ); String notFound = node.attributeValue( "not-found" ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index f4e10225c4..5a450df3f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1601,4 +1601,12 @@ public interface CoreMessageLogger extends BasicLogger { ) void aliasSpecificLockingWithFollowOnLocking(LockMode lockMode); + @LogMessage(level = WARN) + @Message( + value = "embed-xml attributes were intended to be used for DOM4J entity mode. Since that entity mode has been " + + "removed, embed-xml attributes are no longer supported and should be removed from mappings.", + id = 446 + ) + void embedXmlAttributesNoLongerSupported(); + } From 32e87656153f7b0da276b674ff24b1ba25fd195d Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 12 Dec 2012 02:29:23 -0800 Subject: [PATCH 42/58] HHH-7771 : Deprecate obsolete Type, AssociationType, and TypeFactory methods --- .../org/hibernate/mapping/Collection.java | 10 ++ .../java/org/hibernate/mapping/OneToMany.java | 14 +- .../java/org/hibernate/mapping/ToOne.java | 14 +- .../java/org/hibernate/type/ArrayType.java | 11 ++ .../org/hibernate/type/AssociationType.java | 7 +- .../main/java/org/hibernate/type/BagType.java | 9 + .../org/hibernate/type/CollectionType.java | 12 ++ .../hibernate/type/CustomCollectionType.java | 23 ++- .../java/org/hibernate/type/EntityType.java | 35 ++++ .../org/hibernate/type/IdentifierBagType.java | 9 + .../java/org/hibernate/type/ListType.java | 9 + .../org/hibernate/type/ManyToOneType.java | 19 +++ .../main/java/org/hibernate/type/MapType.java | 9 + .../java/org/hibernate/type/OneToOneType.java | 23 ++- .../org/hibernate/type/OrderedMapType.java | 9 + .../org/hibernate/type/OrderedSetType.java | 9 + .../main/java/org/hibernate/type/SetType.java | 9 + .../org/hibernate/type/SortedMapType.java | 11 ++ .../org/hibernate/type/SortedSetType.java | 11 ++ .../main/java/org/hibernate/type/Type.java | 15 +- .../java/org/hibernate/type/TypeFactory.java | 155 ++++++++++++++++++ 21 files changed, 413 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java index 16abb15d66..86a03fbeb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java @@ -612,10 +612,20 @@ public abstract class Collection implements Fetchable, Value, Filterable { this.elementNodeName = elementNodeName; } + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public boolean isEmbedded() { return embedded; } + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public void setEmbedded(boolean embedded) { this.embedded = embedded; } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java index 8b14cfb616..3322e2ad34 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java @@ -150,11 +150,21 @@ public class OneToMany implements Value { //TODO: we could just return all false... throw new UnsupportedOperationException(); } - + + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public boolean isEmbedded() { return embedded; } - + + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public void setEmbedded(boolean embedded) { this.embedded = embedded; } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java index a33fb7355d..9a6ab734f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java @@ -88,11 +88,21 @@ public abstract class ToOne extends SimpleValue implements Fetchable { public Object accept(ValueVisitor visitor) { return visitor.accept(this); } - + + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public boolean isEmbedded() { return embedded; } - + + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public void setEmbedded(boolean embedded) { this.embedded = embedded; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java b/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java index 0c2aef0ab5..893518f3c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ArrayType.java @@ -47,12 +47,23 @@ public class ArrayType extends CollectionType { private final Class elementClass; private final Class arrayClass; + /** + * @deprecated Use {@link #ArrayType(TypeFactory.TypeScope, String, String, Class )} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public ArrayType(TypeFactory.TypeScope typeScope, String role, String propertyRef, Class elementClass, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); this.elementClass = elementClass; arrayClass = Array.newInstance(elementClass, 0).getClass(); } + public ArrayType(TypeFactory.TypeScope typeScope, String role, String propertyRef, Class elementClass) { + super( typeScope, role, propertyRef ); + this.elementClass = elementClass; + arrayClass = Array.newInstance(elementClass, 0).getClass(); + } + public Class getReturnedClass() { return arrayClass; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/AssociationType.java b/hibernate-core/src/main/java/org/hibernate/type/AssociationType.java index de865bbea7..88ed066c4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AssociationType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AssociationType.java @@ -85,7 +85,12 @@ public interface AssociationType extends Type { * no columns to be updated? */ public abstract boolean isAlwaysDirtyChecked(); - + + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated public boolean isEmbeddedInXML(); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/BagType.java b/hibernate-core/src/main/java/org/hibernate/type/BagType.java index 969aa96dd1..08f7ad2a15 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BagType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BagType.java @@ -35,10 +35,19 @@ import org.hibernate.persister.collection.CollectionPersister; public class BagType extends CollectionType { + /** + * @deprecated Use {@link #BagType(TypeFactory.TypeScope, String, String )} + * See Jira issue: HHH-7771 + */ + @Deprecated public BagType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public BagType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) throws HibernateException { return new PersistentBag(session); diff --git a/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java b/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java index d119badddf..4950059ea2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java @@ -81,6 +81,11 @@ public abstract class CollectionType extends AbstractType implements Association private final String foreignKeyPropertyName; private final boolean isEmbeddedInXML; + /** + * @deprecated Use {@link #CollectionType(TypeFactory.TypeScope, String, String)} instead + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType(TypeFactory.TypeScope typeScope, String role, String foreignKeyPropertyName, boolean isEmbeddedInXML) { this.typeScope = typeScope; this.role = role; @@ -88,6 +93,13 @@ public abstract class CollectionType extends AbstractType implements Association this.isEmbeddedInXML = isEmbeddedInXML; } + public CollectionType(TypeFactory.TypeScope typeScope, String role, String foreignKeyPropertyName) { + this.typeScope = typeScope; + this.role = role; + this.foreignKeyPropertyName = foreignKeyPropertyName; + this.isEmbeddedInXML = true; + } + public boolean isEmbeddedInXML() { return isEmbeddedInXML; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/CustomCollectionType.java b/hibernate-core/src/main/java/org/hibernate/type/CustomCollectionType.java index d2cff37443..e0531c4145 100755 --- a/hibernate-core/src/main/java/org/hibernate/type/CustomCollectionType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CustomCollectionType.java @@ -48,6 +48,11 @@ public class CustomCollectionType extends CollectionType { private final UserCollectionType userType; private final boolean customLogging; + /** + * @deprecated Use {@link #CustomCollectionType(TypeFactory.TypeScope, Class, String, String )} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CustomCollectionType( TypeFactory.TypeScope typeScope, Class userTypeClass, @@ -55,13 +60,27 @@ public class CustomCollectionType extends CollectionType { String foreignKeyPropertyName, boolean isEmbeddedInXML) { super( typeScope, role, foreignKeyPropertyName, isEmbeddedInXML ); + userType = createUserCollectionType( userTypeClass ); + customLogging = LoggableUserType.class.isAssignableFrom( userTypeClass ); + } + public CustomCollectionType( + TypeFactory.TypeScope typeScope, + Class userTypeClass, + String role, + String foreignKeyPropertyName) { + super( typeScope, role, foreignKeyPropertyName ); + userType = createUserCollectionType( userTypeClass ); + customLogging = LoggableUserType.class.isAssignableFrom( userTypeClass ); + } + + private static UserCollectionType createUserCollectionType(Class userTypeClass) { if ( !UserCollectionType.class.isAssignableFrom( userTypeClass ) ) { throw new MappingException( "Custom type does not implement UserCollectionType: " + userTypeClass.getName() ); } try { - userType = ( UserCollectionType ) userTypeClass.newInstance(); + return ( UserCollectionType ) userTypeClass.newInstance(); } catch ( InstantiationException ie ) { throw new MappingException( "Cannot instantiate custom type: " + userTypeClass.getName() ); @@ -69,8 +88,6 @@ public class CustomCollectionType extends CollectionType { 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) diff --git a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java index 9265c31a2f..0451e09dcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/EntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EntityType.java @@ -76,7 +76,11 @@ public abstract class EntityType extends AbstractType implements AssociationType * @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". + * + * @deprecated Use {@link #EntityType(TypeFactory.TypeScope, String, String, boolean, boolean )} instead. + * See Jira issue: HHH-7771 */ + @Deprecated protected EntityType( TypeFactory.TypeScope scope, String entityName, @@ -92,6 +96,32 @@ public abstract class EntityType extends AbstractType implements AssociationType this.unwrapProxy = unwrapProxy; } + /** + * Constructs the requested entity type mapping. + * + * @param scope The type scope + * @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 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( + TypeFactory.TypeScope scope, + String entityName, + String uniqueKeyPropertyName, + boolean eager, + boolean unwrapProxy) { + this.scope = scope; + this.associatedEntityName = entityName; + this.uniqueKeyPropertyName = uniqueKeyPropertyName; + this.isEmbeddedInXML = true; + this.eager = eager; + this.unwrapProxy = unwrapProxy; + } + protected TypeFactory.TypeScope scope() { return scope; } @@ -476,6 +506,11 @@ public abstract class EntityType extends AbstractType implements AssociationType } } + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ + @Deprecated protected boolean isNotEmbedded(SessionImplementor session) { // return !isEmbeddedInXML; return false; diff --git a/hibernate-core/src/main/java/org/hibernate/type/IdentifierBagType.java b/hibernate-core/src/main/java/org/hibernate/type/IdentifierBagType.java index 306a815884..2d34237b95 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/IdentifierBagType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/IdentifierBagType.java @@ -34,10 +34,19 @@ import org.hibernate.persister.collection.CollectionPersister; public class IdentifierBagType extends CollectionType { + /** + * @deprecated Use {@link #IdentifierBagType(org.hibernate.type.TypeFactory.TypeScope, String, String)} + * See Jira issue: HHH-7771 + */ + @Deprecated public IdentifierBagType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public IdentifierBagType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + public PersistentCollection instantiate( SessionImplementor session, CollectionPersister persister, Serializable key) diff --git a/hibernate-core/src/main/java/org/hibernate/type/ListType.java b/hibernate-core/src/main/java/org/hibernate/type/ListType.java index fd825d9727..b7d9824b4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ListType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ListType.java @@ -34,10 +34,19 @@ import org.hibernate.persister.collection.CollectionPersister; public class ListType extends CollectionType { + /** + * @deprecated Use {@link #ListType(org.hibernate.type.TypeFactory.TypeScope, String, String)} + * See Jira issue: HHH-7771 + */ + @Deprecated public ListType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public ListType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) { return new PersistentList(session); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java index 446dbf3467..c7cd51db99 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java @@ -70,6 +70,12 @@ public class ManyToOneType extends EntityType { this( scope, referencedEntityName, null, lazy, true, false, false, false ); } + + /** + * @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, String, boolean, boolean, boolean, boolean ) } instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public ManyToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -84,6 +90,19 @@ public class ManyToOneType extends EntityType { this.isLogicalOneToOne = isLogicalOneToOne; } + public ManyToOneType( + TypeFactory.TypeScope scope, + String referencedEntityName, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + boolean ignoreNotFound, + boolean isLogicalOneToOne) { + super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy ); + this.ignoreNotFound = ignoreNotFound; + this.isLogicalOneToOne = isLogicalOneToOne; + } + protected boolean isNullable() { return ignoreNotFound; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/MapType.java b/hibernate-core/src/main/java/org/hibernate/type/MapType.java index 38a737b23d..f0aa1d1447 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/MapType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/MapType.java @@ -37,10 +37,19 @@ import org.hibernate.persister.collection.CollectionPersister; public class MapType extends CollectionType { + /** + * @deprecated Use {@link #MapType(TypeFactory.TypeScope, String, String ) } instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public MapType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public MapType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) { return new PersistentMap(session); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java b/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java index ccf543cae3..838955bf72 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java @@ -47,6 +47,12 @@ public class OneToOneType extends EntityType { private final String propertyName; private final String entityName; + /** + * @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, String, boolean, boolean, String, String)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public OneToOneType( TypeFactory.TypeScope scope, String referencedEntityName, @@ -62,7 +68,22 @@ public class OneToOneType extends EntityType { this.propertyName = propertyName; this.entityName = entityName; } - + + public OneToOneType( + TypeFactory.TypeScope scope, + String referencedEntityName, + ForeignKeyDirection foreignKeyType, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { + super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy ); + this.foreignKeyType = foreignKeyType; + this.propertyName = propertyName; + this.entityName = entityName; + } + public String getPropertyName() { return propertyName; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/OrderedMapType.java b/hibernate-core/src/main/java/org/hibernate/type/OrderedMapType.java index 708be71586..b00b1eaf4b 100755 --- a/hibernate-core/src/main/java/org/hibernate/type/OrderedMapType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/OrderedMapType.java @@ -36,11 +36,20 @@ public class OrderedMapType extends MapType { * @param role The collection role name. * @param propertyRef The property ref name. * @param isEmbeddedInXML Is this collection to embed itself in xml + * + * @deprecated Use {@link #OrderedMapType(TypeFactory.TypeScope, String, String)} instead. + * instead. + * See Jira issue: HHH-7771 */ + @Deprecated public OrderedMapType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public OrderedMapType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + /** * {@inheritDoc} */ diff --git a/hibernate-core/src/main/java/org/hibernate/type/OrderedSetType.java b/hibernate-core/src/main/java/org/hibernate/type/OrderedSetType.java index 970b01821e..b9aae70074 100755 --- a/hibernate-core/src/main/java/org/hibernate/type/OrderedSetType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/OrderedSetType.java @@ -37,11 +37,20 @@ public class OrderedSetType extends SetType { * @param role The collection role name. * @param propertyRef The property ref name. * @param isEmbeddedInXML Is this collection to embed itself in xml + * + * @deprecated Use {@link #OrderedSetType(org.hibernate.type.TypeFactory.TypeScope, String, String)} + * instead. + * See Jira issue: HHH-7771 */ + @Deprecated public OrderedSetType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public OrderedSetType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + /** * {@inheritDoc} */ diff --git a/hibernate-core/src/main/java/org/hibernate/type/SetType.java b/hibernate-core/src/main/java/org/hibernate/type/SetType.java index 52b03dbd70..e1b6bd3242 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/SetType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SetType.java @@ -33,10 +33,19 @@ import org.hibernate.persister.collection.CollectionPersister; public class SetType extends CollectionType { + /** + * @deprecated Use {@link #SetType(org.hibernate.type.TypeFactory.TypeScope, String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public SetType(TypeFactory.TypeScope typeScope, String role, String propertyRef, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); } + public SetType(TypeFactory.TypeScope typeScope, String role, String propertyRef) { + super( typeScope, role, propertyRef ); + } + public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) { return new PersistentSet(session); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/SortedMapType.java b/hibernate-core/src/main/java/org/hibernate/type/SortedMapType.java index 172bcb2fd0..1087ea0add 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/SortedMapType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SortedMapType.java @@ -37,11 +37,22 @@ public class SortedMapType extends MapType { private final Comparator comparator; + /** + * @deprecated Use {@link #SortedMapType(org.hibernate.type.TypeFactory.TypeScope, String, String, java.util.Comparator)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public SortedMapType(TypeFactory.TypeScope typeScope, String role, String propertyRef, Comparator comparator, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); this.comparator = comparator; } + public SortedMapType(TypeFactory.TypeScope typeScope, String role, String propertyRef, Comparator comparator) { + super( typeScope, role, propertyRef ); + this.comparator = comparator; + } + public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) { PersistentSortedMap map = new PersistentSortedMap(session); map.setComparator(comparator); diff --git a/hibernate-core/src/main/java/org/hibernate/type/SortedSetType.java b/hibernate-core/src/main/java/org/hibernate/type/SortedSetType.java index 8e78e05436..eeb6ff48bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/SortedSetType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/SortedSetType.java @@ -35,11 +35,22 @@ import org.hibernate.persister.collection.CollectionPersister; public class SortedSetType extends SetType { private final Comparator comparator; + /** + * @deprecated Use {@link #SortedSetType(org.hibernate.type.TypeFactory.TypeScope, String, String, java.util.Comparator)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public SortedSetType(TypeFactory.TypeScope typeScope, String role, String propertyRef, Comparator comparator, boolean isEmbeddedInXML) { super( typeScope, role, propertyRef, isEmbeddedInXML ); this.comparator = comparator; } + public SortedSetType(TypeFactory.TypeScope typeScope, String role, String propertyRef, Comparator comparator) { + super( typeScope, role, propertyRef ); + this.comparator = comparator; + } + public PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key) { PersistentSortedSet set = new PersistentSortedSet(session); set.setComparator(comparator); diff --git a/hibernate-core/src/main/java/org/hibernate/type/Type.java b/hibernate-core/src/main/java/org/hibernate/type/Type.java index d2b3aa058d..3a75ac8b78 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/Type.java +++ b/hibernate-core/src/main/java/org/hibernate/type/Type.java @@ -166,8 +166,13 @@ public interface Type extends Serializable { * @return The java type class handled by this type. */ public Class getReturnedClass(); - + + /** + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 + */ @SuppressWarnings( {"UnusedDeclaration"}) + @Deprecated public boolean isXMLElement(); /** @@ -392,7 +397,11 @@ public interface Type extends Serializable { * @param factory The session factory * * @throws HibernateException An error from Hibernate + * + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 */ + @Deprecated public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException; @@ -405,7 +414,11 @@ public interface Type extends Serializable { * @return an instance of the {@link #getReturnedClass() mapped class} * * @throws HibernateException An error from Hibernate + * + * @deprecated To be removed in 5. Removed as part of removing the notion of DOM entity-mode. + * See Jira issue: HHH-7771 */ + @Deprecated public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException; /** diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java b/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java index 5086bd846e..131251349c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java @@ -155,6 +155,12 @@ public final class TypeFactory implements Serializable { } } + /** + * @deprecated Use {@link #customCollection(String, java.util.Properties, String, String)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType customCollection( String typeName, Properties typeParameters, @@ -175,6 +181,25 @@ public final class TypeFactory implements Serializable { return result; } + public CollectionType customCollection( + String typeName, + Properties typeParameters, + String role, + String propertyRef) { + 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( typeScope, typeClass, role, propertyRef ); + if ( typeParameters != null ) { + injectParameters( result.getUserType(), typeParameters ); + } + return result; + } + public CustomType custom(Class typeClass, Properties parameters) { return custom( typeClass, parameters, typeScope ); } @@ -209,6 +234,12 @@ public final class TypeFactory implements Serializable { // one-to-one type builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * @deprecated Use {@link #oneToOne(String, ForeignKeyDirection, String, boolean, boolean, String, String)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public EntityType oneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, @@ -222,6 +253,18 @@ public final class TypeFactory implements Serializable { lazy, unwrapProxy, isEmbeddedInXML, entityName, propertyName ); } + public EntityType oneToOne( + String persistentClass, + ForeignKeyDirection foreignKeyType, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + String entityName, + String propertyName) { + return new OneToOneType( typeScope, persistentClass, foreignKeyType, uniqueKeyPropertyName, + lazy, unwrapProxy, entityName, propertyName ); + } + public EntityType specialOneToOne( String persistentClass, ForeignKeyDirection foreignKeyType, @@ -245,6 +288,12 @@ public final class TypeFactory implements Serializable { return new ManyToOneType( typeScope, persistentClass, lazy ); } + /** + * @deprecated Use {@link #manyToOne(String, String, boolean, boolean, boolean, boolean)} + * instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public EntityType manyToOne( String persistentClass, String uniqueKeyPropertyName, @@ -265,49 +314,155 @@ public final class TypeFactory implements Serializable { ); } + public EntityType manyToOne( + String persistentClass, + String uniqueKeyPropertyName, + boolean lazy, + boolean unwrapProxy, + boolean ignoreNotFound, + boolean isLogicalOneToOne) { + return new ManyToOneType( + typeScope, + persistentClass, + uniqueKeyPropertyName, + lazy, + unwrapProxy, + ignoreNotFound, + isLogicalOneToOne + ); + } // collection type builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * @deprecated Use {@link #array(String, String, Class)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType array(String role, String propertyRef, boolean embedded, Class elementClass) { return new ArrayType( typeScope, role, propertyRef, elementClass, embedded ); } + public CollectionType array(String role, String propertyRef, Class elementClass) { + return new ArrayType( typeScope, role, propertyRef, elementClass ); + } + + /** + * @deprecated Use {@link #list(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType list(String role, String propertyRef, boolean embedded) { return new ListType( typeScope, role, propertyRef, embedded ); } + public CollectionType list(String role, String propertyRef) { + return new ListType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #bag(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType bag(String role, String propertyRef, boolean embedded) { return new BagType( typeScope, role, propertyRef, embedded ); } + public CollectionType bag(String role, String propertyRef) { + return new BagType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #idbag(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType idbag(String role, String propertyRef, boolean embedded) { return new IdentifierBagType( typeScope, role, propertyRef, embedded ); } + public CollectionType idbag(String role, String propertyRef) { + return new IdentifierBagType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #map(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType map(String role, String propertyRef, boolean embedded) { return new MapType( typeScope, role, propertyRef, embedded ); } + public CollectionType map(String role, String propertyRef) { + return new MapType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #orderedMap(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType orderedMap(String role, String propertyRef, boolean embedded) { return new OrderedMapType( typeScope, role, propertyRef, embedded ); } + public CollectionType orderedMap(String role, String propertyRef) { + return new OrderedMapType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #sortedMap(String, String, java.util.Comparator)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType sortedMap(String role, String propertyRef, boolean embedded, Comparator comparator) { return new SortedMapType( typeScope, role, propertyRef, comparator, embedded ); } + public CollectionType sortedMap(String role, String propertyRef, Comparator comparator) { + return new SortedMapType( typeScope, role, propertyRef, comparator ); + } + + /** + * @deprecated Use {@link #set(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType set(String role, String propertyRef, boolean embedded) { return new SetType( typeScope, role, propertyRef, embedded ); } + public CollectionType set(String role, String propertyRef) { + return new SetType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #orderedSet(String, String)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType orderedSet(String role, String propertyRef, boolean embedded) { return new OrderedSetType( typeScope, role, propertyRef, embedded ); } + public CollectionType orderedSet(String role, String propertyRef) { + return new OrderedSetType( typeScope, role, propertyRef ); + } + + /** + * @deprecated Use {@link #sortedSet(String, String, java.util.Comparator)} instead. + * See Jira issue: HHH-7771 + */ + @Deprecated public CollectionType sortedSet(String role, String propertyRef, boolean embedded, Comparator comparator) { return new SortedSetType( typeScope, role, propertyRef, comparator, embedded ); } + public CollectionType sortedSet(String role, String propertyRef, Comparator comparator) { + return new SortedSetType( typeScope, role, propertyRef, comparator ); + } // component type builders ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From cb4e92a98d6e6cd3cf8ba23ba7b6b6f18f4f455f Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 12 Dec 2012 10:37:40 -0800 Subject: [PATCH 43/58] HHH-7771 : Deprecate obsolete Type, AssociationType, and TypeFactory methods --- .../src/main/java/org/hibernate/mapping/Array.java | 2 +- .../src/main/java/org/hibernate/mapping/Bag.java | 2 +- .../main/java/org/hibernate/mapping/Collection.java | 2 +- .../java/org/hibernate/mapping/IdentifierBag.java | 2 +- .../src/main/java/org/hibernate/mapping/List.java | 2 +- .../main/java/org/hibernate/mapping/ManyToOne.java | 1 - .../src/main/java/org/hibernate/mapping/Map.java | 6 +++--- .../main/java/org/hibernate/mapping/OneToMany.java | 1 - .../src/main/java/org/hibernate/mapping/OneToOne.java | 1 - .../src/main/java/org/hibernate/mapping/Set.java | 6 +++--- .../source/internal/HibernateTypeResolver.java | 11 +++-------- 11 files changed, 14 insertions(+), 22 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Array.java b/hibernate-core/src/main/java/org/hibernate/mapping/Array.java index b6e565a13a..5f6700f7a2 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Array.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Array.java @@ -63,7 +63,7 @@ public class Array extends List { public CollectionType getDefaultCollectionType() throws MappingException { return getMappings().getTypeResolver() .getTypeFactory() - .array( getRole(), getReferencedPropertyName(), isEmbedded(), getElementClass() ); + .array( getRole(), getReferencedPropertyName(), getElementClass() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Bag.java b/hibernate-core/src/main/java/org/hibernate/mapping/Bag.java index 326cc22f6e..a01b24fd37 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Bag.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Bag.java @@ -39,7 +39,7 @@ public class Bag extends Collection { public CollectionType getDefaultCollectionType() { return getMappings().getTypeResolver() .getTypeFactory() - .bag( getRole(), getReferencedPropertyName(), isEmbedded() ); + .bag( getRole(), getReferencedPropertyName() ); } void createPrimaryKey() { diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java index 86a03fbeb8..51dbd0e25b 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Collection.java @@ -384,7 +384,7 @@ public abstract class Collection implements Fetchable, Value, Filterable { else { return mappings.getTypeResolver() .getTypeFactory() - .customCollection( typeName, typeParameters, role, referencedPropertyName, isEmbedded() ); + .customCollection( typeName, typeParameters, role, referencedPropertyName ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/IdentifierBag.java b/hibernate-core/src/main/java/org/hibernate/mapping/IdentifierBag.java index ed8f1ed0ea..a1164173fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/IdentifierBag.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/IdentifierBag.java @@ -38,7 +38,7 @@ public class IdentifierBag extends IdentifierCollection { public CollectionType getDefaultCollectionType() { return getMappings().getTypeResolver() .getTypeFactory() - .idbag( getRole(), getReferencedPropertyName(), isEmbedded() ); + .idbag( getRole(), getReferencedPropertyName() ); } public Object accept(ValueVisitor visitor) { diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/List.java b/hibernate-core/src/main/java/org/hibernate/mapping/List.java index 132d708204..a034491401 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/List.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/List.java @@ -46,7 +46,7 @@ public class List extends IndexedCollection { public CollectionType getDefaultCollectionType() throws MappingException { return getMappings().getTypeResolver() .getTypeFactory() - .list( getRole(), getReferencedPropertyName(), isEmbedded() ); + .list( getRole(), getReferencedPropertyName() ); } public Object accept(ValueVisitor visitor) { diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java index e013a9eba2..9736b5bf51 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java @@ -49,7 +49,6 @@ public class ManyToOne extends ToOne { getReferencedPropertyName(), isLazy(), isUnwrapProxy(), - isEmbedded(), isIgnoreNotFound(), isLogicalOneToOne ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Map.java b/hibernate-core/src/main/java/org/hibernate/mapping/Map.java index 3751eae2ce..b196a5af22 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Map.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Map.java @@ -44,17 +44,17 @@ public class Map extends IndexedCollection { if ( isSorted() ) { return getMappings().getTypeResolver() .getTypeFactory() - .sortedMap( getRole(), getReferencedPropertyName(), isEmbedded(), getComparator() ); + .sortedMap( getRole(), getReferencedPropertyName(), getComparator() ); } else if ( hasOrder() ) { return getMappings().getTypeResolver() .getTypeFactory() - .orderedMap( getRole(), getReferencedPropertyName(), isEmbedded() ); + .orderedMap( getRole(), getReferencedPropertyName() ); } else { return getMappings().getTypeResolver() .getTypeFactory() - .map( getRole(), getReferencedPropertyName(), isEmbedded() ); + .map( getRole(), getReferencedPropertyName() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java index 3322e2ad34..5abfefd081 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java @@ -51,7 +51,6 @@ public class OneToMany implements Value { null, false, false, - isEmbedded(), isIgnoreNotFound(), false ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java b/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java index 74102aa9e7..ca8e196db6 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java @@ -84,7 +84,6 @@ public class OneToOne extends ToOne { referencedPropertyName, isLazy(), isUnwrapProxy(), - isEmbedded(), entityName, propertyName ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Set.java b/hibernate-core/src/main/java/org/hibernate/mapping/Set.java index c6a91012e6..2c77bf1e28 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Set.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Set.java @@ -61,17 +61,17 @@ public class Set extends Collection { if ( isSorted() ) { return getMappings().getTypeResolver() .getTypeFactory() - .sortedSet( getRole(), getReferencedPropertyName(), isEmbedded(), getComparator() ); + .sortedSet( getRole(), getReferencedPropertyName(), getComparator() ); } else if ( hasOrder() ) { return getMappings().getTypeResolver() .getTypeFactory() - .orderedSet( getRole(), getReferencedPropertyName(), isEmbedded() ); + .orderedSet( getRole(), getReferencedPropertyName() ); } else { return getMappings().getTypeResolver() .getTypeFactory() - .set( getRole(), getReferencedPropertyName(), isEmbedded() ); + .set( getRole(), getReferencedPropertyName() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/HibernateTypeResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/HibernateTypeResolver.java index d8379db561..84fde97807 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/HibernateTypeResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/HibernateTypeResolver.java @@ -159,9 +159,7 @@ class HibernateTypeResolver { typeName, getTypeParameters( attributeBinding.getHibernateTypeDescriptor() ), attributeBinding.getAttribute().getName(), - attributeBinding.getReferencedPropertyName(), - attributeBinding.getCollectionElement().getCollectionElementNature() == - CollectionElementNature.COMPOSITE + attributeBinding.getReferencedPropertyName() ); } else { @@ -182,16 +180,13 @@ class HibernateTypeResolver { case SET: { return typeFactory.set( attributeBinding.getAttribute().getName(), - attributeBinding.getReferencedPropertyName(), - attributeBinding.getCollectionElement().getCollectionElementNature() == CollectionElementNature.COMPOSITE + attributeBinding.getReferencedPropertyName() ); } case BAG: { return typeFactory.bag( attributeBinding.getAttribute().getName(), - attributeBinding.getReferencedPropertyName(), - attributeBinding.getCollectionElement() - .getCollectionElementNature() == CollectionElementNature.COMPOSITE + attributeBinding.getReferencedPropertyName() ); } default: { From 3a995f574dfa89682d4b70a23fd786dba8c859a6 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Fri, 7 Dec 2012 16:19:54 -0500 Subject: [PATCH 44/58] HHH-7797 Block "unique" syntax on a column if a constraint can/will be used --- .../java/org/hibernate/dialect/Dialect.java | 5 +++ .../java/org/hibernate/mapping/Table.java | 31 +++++++++------ .../java/org/hibernate/mapping/UniqueKey.java | 32 ++++++++-------- .../hibernate/metamodel/relational/Table.java | 18 +++++---- .../metamodel/relational/UniqueKey.java | 38 ++++++++----------- 5 files changed, 67 insertions(+), 57 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 1b8cd205c4..dab857b72c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2056,6 +2056,11 @@ public abstract class Dialect implements ConversionContext { return true; } + /** + * Does this dialect support columns that are both unique and not null. + * + * @return True if supported, false otherwise. + */ public boolean supportsNotNullUnique() { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 971154eb25..cbcbe436ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -422,10 +422,13 @@ public class Table implements RelationalModel, Serializable { alter.append( " not null" ); } - boolean useUniqueConstraint = column.isUnique() && - dialect.supportsUnique() && - ( column.isNullable() || dialect.supportsNotNullUnique() ); - if ( useUniqueConstraint ) { + // If the column is 1.) unique, 2.) the dialect supports the + // unique syntax, 3.) the column is nullable or supports + // "not null unique", and 4.) a constraint will not be created + if ( column.isUnique() && dialect.supportsUnique() + && ( column.isNullable() + || dialect.supportsNotNullUnique() ) + && !dialect.supportsUniqueConstraintInCreateAlterTable() ) { alter.append( " unique" ); } @@ -523,17 +526,21 @@ public class Table implements RelationalModel, Serializable { } } - - boolean useUniqueConstraint = col.isUnique() && - ( col.isNullable() || dialect.supportsNotNullUnique() ); - if ( useUniqueConstraint ) { - if ( dialect.supportsUnique() ) { - buf.append( " unique" ); - } - else { + + // If the column is 1.) unique and nullable or 2.) unique, + // not null, and the dialect supports unique not null + if ( col.isUnique() + && ( col.isNullable() + || dialect.supportsNotNullUnique() ) ) { + if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { + // If the constraint is supported, do not add to the column syntax. UniqueKey uk = getOrCreateUniqueKey( col.getQuotedName( dialect ) + '_' ); uk.addColumn( col ); } + else if ( dialect.supportsUnique() ) { + // Otherwise, add to the column syntax if supported. + buf.append( " unique" ); + } } if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java index f703f88a4c..36cd559c6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java @@ -35,23 +35,21 @@ import org.hibernate.engine.spi.Mapping; public class UniqueKey extends Constraint { public String sqlConstraintString(Dialect dialect) { + // TODO: This may not be necessary, but not all callers currently + // check it on their own. Go through their logic. + if ( !isGenerated( dialect ) ) return null; + StringBuilder buf = new StringBuilder( "unique (" ); - boolean hadNullableColumn = false; Iterator iter = getColumnIterator(); while ( iter.hasNext() ) { Column column = (Column) iter.next(); - if ( !hadNullableColumn && column.isNullable() ) { - hadNullableColumn = 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 !hadNullableColumn || dialect.supportsNotNullUnique() ? - buf.append( ')' ).toString() : - null; + + return buf.append( ')' ).toString(); } @Override @@ -60,18 +58,19 @@ public class UniqueKey extends Constraint { String constraintName, String defaultCatalog, String defaultSchema) { + // TODO: This may not be necessary, but not all callers currently + // check it on their own. Go through their logic. + if ( !isGenerated( dialect ) ) return null; + StringBuilder buf = new StringBuilder( - dialect.getAddUniqueConstraintString( constraintName ) - ).append( '(' ); + dialect.getAddUniqueConstraintString( 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() ? buf.append( ')' ).toString() : null; + return buf.append( ')' ).toString(); } @Override @@ -97,12 +96,13 @@ public class UniqueKey extends Constraint { @Override public boolean isGenerated(Dialect dialect) { + if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return false; if ( dialect.supportsNotNullUnique() ) return true; + Iterator iter = getColumnIterator(); while ( iter.hasNext() ) { - if ( ( (Column) iter.next() ).isNullable() ) { - return false; - } + // Dialect does not support "not null unique" and this column is not null. + if ( ! ( (Column) iter.next() ).isNullable() ) return false; } return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 4d7562a776..72ce0df0f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -199,16 +199,20 @@ public class Table extends AbstractTableSpecification implements Exportable { } - boolean useUniqueConstraint = col.isUnique() && - ( col.isNullable() || dialect.supportsNotNullUnique() ); - if ( useUniqueConstraint ) { - if ( dialect.supportsUnique() ) { - buf.append( " unique" ); - } - else { + // If the column is 1.) unique and nullable or 2.) unique, + // not null, and the dialect supports unique not null + if ( col.isUnique() + && ( col.isNullable() + || dialect.supportsNotNullUnique() ) ) { + if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { + // If the constraint is supported, do not add to the column syntax. UniqueKey uk = getOrCreateUniqueKey( col.getColumnName().encloseInQuotesIfQuoted( dialect ) + '_' ); uk.addColumn( col ); } + else if ( dialect.supportsUnique() ) { + // Otherwise, add to the column syntax if supported. + buf.append( " unique" ); + } } if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java index 4fbffeba1e..1c5413e473 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java @@ -48,21 +48,22 @@ public class UniqueKey extends AbstractConstraint implements Constraint { @Override public boolean isCreationVetoed(Dialect dialect) { - if ( dialect.supportsNotNullUnique() ) { - return false; - } - + if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return true; + if ( dialect.supportsNotNullUnique() ) return false; + for ( Column column : getColumns() ) { - if ( column.isNullable() ) { - return true; - } + // Dialect does not support "not null unique" and this column is not null. + if ( ! column.isNullable() ) return true; } return false; } public String sqlConstraintStringInCreateTable(Dialect dialect) { + // TODO: This may not be necessary, but not all callers currently + // check it on their own. Go through their logic. + if ( isCreationVetoed( dialect ) ) return null; + StringBuilder buf = new StringBuilder( "unique (" ); - boolean hadNullableColumn = false; boolean first = true; for ( Column column : getColumns() ) { if ( first ) { @@ -71,23 +72,19 @@ public class UniqueKey extends AbstractConstraint implements Constraint { else { buf.append( ", " ); } - if ( !hadNullableColumn && column.isNullable() ) { - hadNullableColumn = true; - } buf.append( column.getColumnName().encloseInQuotesIfQuoted( dialect ) ); } - //do not add unique constraint on DB not supporting unique and nullable columns - return !hadNullableColumn || dialect.supportsNotNullUnique() ? - buf.append( ')' ).toString() : - null; + return buf.append( ')' ).toString(); } @Override public String sqlConstraintStringInAlterTable(Dialect dialect) { + // TODO: This may not be necessary, but not all callers currently + // check it on their own. Go through their logic. + if ( isCreationVetoed( dialect ) ) return null; + StringBuilder buf = new StringBuilder( - dialect.getAddUniqueConstraintString( getName() ) - ).append( '(' ); - boolean nullable = false; + dialect.getAddUniqueConstraintString( getName() ) ).append( '(' ); boolean first = true; for ( Column column : getColumns() ) { if ( first ) { @@ -96,11 +93,8 @@ public class UniqueKey extends AbstractConstraint implements Constraint { else { buf.append( ", " ); } - if ( !nullable && column.isNullable() ) { - nullable = true; - } buf.append( column.getColumnName().encloseInQuotesIfQuoted( dialect ) ); } - return !nullable || dialect.supportsNotNullUnique() ? buf.append( ')' ).toString() : null; + return buf.append( ')' ).toString(); } } From 953aec40adaf5e172eb6c936df71395868732b5d Mon Sep 17 00:00:00 2001 From: brmeyer Date: Tue, 11 Dec 2012 10:36:21 -0500 Subject: [PATCH 45/58] HHH-7797 UniqueDelegate & DefaultUniqueDelegate --- .../java/org/hibernate/dialect/Dialect.java | 46 +++------- .../dialect/unique/DefaultUniqueDelegate.java | 89 +++++++++++++++++++ .../dialect/unique/UniqueDelegate.java | 45 ++++++++++ .../java/org/hibernate/mapping/Table.java | 42 +-------- 4 files changed, 147 insertions(+), 75 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index dab857b72c..bc869b59bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -58,6 +58,8 @@ import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy; import org.hibernate.dialect.lock.SelectLockingStrategy; import org.hibernate.dialect.pagination.LegacyLimitHandler; import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.unique.DefaultUniqueDelegate; +import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionImplementor; @@ -115,6 +117,8 @@ public abstract class Dialect implements ConversionContext { private final Properties properties = new Properties(); private final Map sqlFunctions = new HashMap(); private final Set sqlKeywords = new HashSet(); + + private final UniqueDelegate uniqueDelegate; // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -203,6 +207,8 @@ public abstract class Dialect implements ConversionContext { registerHibernateType( Types.BLOB, StandardBasicTypes.BLOB.getName() ); registerHibernateType( Types.CLOB, StandardBasicTypes.CLOB.getName() ); registerHibernateType( Types.REAL, StandardBasicTypes.FLOAT.getName() ); + + uniqueDelegate = new DefaultUniqueDelegate( this ); } /** @@ -1907,23 +1913,6 @@ public abstract class Dialect implements ConversionContext { return true; } - /** - * Does this dialect support the UNIQUE 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). * @@ -1989,16 +1978,6 @@ public abstract class Dialect implements ConversionContext { return " add constraint " + constraintName + " primary key "; } - /** - * The syntax used to add a unique constraint to a table. - * - * @param constraintName The name of the unique constraint. - * @return The "add unique" fragment - */ - public String getAddUniqueConstraintString(String constraintName) { - return " add constraint " + constraintName + " unique "; - } - public boolean hasSelfReferentialForeignKeyBug() { return false; } @@ -2056,15 +2035,6 @@ public abstract class Dialect implements ConversionContext { return true; } - /** - * Does this dialect support columns that are both unique and not null. - * - * @return True if supported, false otherwise. - */ - public boolean supportsNotNullUnique() { - return true; - } - /** * Completely optional cascading drop clause * @@ -2416,4 +2386,8 @@ public abstract class Dialect implements ConversionContext { public boolean useFollowOnLocking() { return false; } + + public UniqueDelegate getUniqueDelegate() { + return uniqueDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java new file mode 100644 index 0000000000..dfcc88ae65 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -0,0 +1,89 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.dialect.unique; + +import java.util.Iterator; +import java.util.List; + +import org.hibernate.dialect.Dialect; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Table; +import org.hibernate.mapping.UniqueKey; + +/** + * The default UniqueDelegate implementation for most dialects. Uses + * separate create/alter statements to apply uniqueness to a column. + * + * @author Brett Meyer + */ +public class DefaultUniqueDelegate implements UniqueDelegate { + + private final Dialect dialect; + + public DefaultUniqueDelegate( Dialect dialect ) { + this.dialect = dialect; + } + + @Override + public void applyUnique( Table table, Column column, StringBuilder sb ) { +// if ( column.isUnique() +// && ( column.isNullable() +// || dialect.supportsNotNullUnique() ) ) { +// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { +// // If the constraint is supported, do not add to the column syntax. +// UniqueKey uk = getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); +// uk.addColumn( column ); +// } +// else if ( dialect.supportsUnique() ) { +// // Otherwise, add to the column syntax if supported. +// sb.append( " unique" ); +// } +// } + + UniqueKey uk = table.getOrCreateUniqueKey( + column.getQuotedName( dialect ) + '_' ); + uk.addColumn( column ); + } + + @Override + public void createUniqueConstraint( Table table, StringBuilder sb ) { +// 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 ukiter = table.getUniqueKeyIterator(); + while ( ukiter.hasNext() ) { + UniqueKey uk = (UniqueKey) ukiter.next(); + String constraint = uk.sqlConstraintString( dialect ); + if ( constraint != null ) { + sb.append( ", " ).append( constraint ); + } + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java new file mode 100644 index 0000000000..a173c22c8c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.dialect.unique; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Table; + +/** + * Dialect-level delegate in charge of applying "uniqueness" to a column. + * Uniqueness can be defined in 1 of 3 ways: + * + * 1.) Add a unique constraint via separate create/alter table statements. + * 2.) Add a unique constraint via dialect-specific syntax in table create statement. + * 3.) Add "unique" syntax to the column itself. + * + * #1 & #2 are preferred, if possible -- #3 should be solely a fall-back. + * + * See HHH-7797. + * + * @author Brett Meyer + */ +public interface UniqueDelegate { + + public void applyUnique( Table table, Column column, StringBuilder sb ); + + public void createUniqueConstraint( Table table, StringBuilder sb ); +} diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index cbcbe436ef..87c2148821 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -422,15 +422,7 @@ public class Table implements RelationalModel, Serializable { alter.append( " not null" ); } - // If the column is 1.) unique, 2.) the dialect supports the - // unique syntax, 3.) the column is nullable or supports - // "not null unique", and 4.) a constraint will not be created - if ( column.isUnique() && dialect.supportsUnique() - && ( column.isNullable() - || dialect.supportsNotNullUnique() ) - && !dialect.supportsUniqueConstraintInCreateAlterTable() ) { - alter.append( " unique" ); - } + dialect.getUniqueDelegate().applyUnique( this, column, alter ); if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) { alter.append( " check(" ) @@ -527,21 +519,7 @@ public class Table implements RelationalModel, Serializable { } - // If the column is 1.) unique and nullable or 2.) unique, - // not null, and the dialect supports unique not null - if ( col.isUnique() - && ( col.isNullable() - || dialect.supportsNotNullUnique() ) ) { - if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { - // If the constraint is supported, do not add to the column syntax. - UniqueKey uk = getOrCreateUniqueKey( col.getQuotedName( dialect ) + '_' ); - uk.addColumn( col ); - } - else if ( dialect.supportsUnique() ) { - // Otherwise, add to the column syntax if supported. - buf.append( " unique" ); - } - } + dialect.getUniqueDelegate().applyUnique( this, col, buf ); if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { buf.append( " check (" ) @@ -564,21 +542,7 @@ public class Table implements RelationalModel, Serializable { .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) ); - }*/ + dialect.getUniqueDelegate().createUniqueConstraint( this, buf ); if ( dialect.supportsTableCheck() ) { Iterator chiter = checkConstraints.iterator(); From 4204f2c5fef24c87808b21c537011f25e26e3eb0 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Tue, 11 Dec 2012 14:00:48 -0500 Subject: [PATCH 46/58] HHH-7797 Finished first take on UniqueDelegate and rolled into .cfg and .mapping --- .../java/org/hibernate/cfg/Configuration.java | 14 ++-- .../org/hibernate/dialect/DB2Dialect.java | 5 -- .../dialect/unique/DefaultUniqueDelegate.java | 82 ++++++++++++++---- .../dialect/unique/UniqueDelegate.java | 55 +++++++++++- .../java/org/hibernate/mapping/Table.java | 13 +-- .../java/org/hibernate/mapping/UniqueKey.java | 83 +++++-------------- 6 files changed, 154 insertions(+), 98 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index e31728cec8..f5e59e02f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -1049,17 +1049,15 @@ public class Configuration implements Serializable { 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.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(); + subIter = table.getIndexIterator(); while ( subIter.hasNext() ) { Index index = (Index) subIter.next(); script.add( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 3bfa7a7695..01f13b7883 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -30,7 +30,6 @@ import java.sql.Types; import org.hibernate.JDBCException; import org.hibernate.cfg.Environment; -import org.hibernate.dialect.function.AnsiTrimEmulationFunction; import org.hibernate.dialect.function.AvgWithArgumentCastFunction; import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; @@ -279,10 +278,6 @@ public class DB2Dialect extends Dialect { return false; } @Override - public boolean supportsNotNullUnique() { - return false; - } - @Override public boolean supportsExistsInSelect() { return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index dfcc88ae65..a9c8ac33fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -21,7 +21,6 @@ package org.hibernate.dialect.unique; import java.util.Iterator; -import java.util.List; import org.hibernate.dialect.Dialect; import org.hibernate.mapping.Column; @@ -43,7 +42,7 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public void applyUnique( Table table, Column column, StringBuilder sb ) { + public String applyUniqueToColumn( Table table, Column column ) { // if ( column.isUnique() // && ( column.isNullable() // || dialect.supportsNotNullUnique() ) ) { @@ -61,29 +60,78 @@ public class DefaultUniqueDelegate implements UniqueDelegate { UniqueKey uk = table.getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); uk.addColumn( column ); + return ""; } @Override - public void createUniqueConstraint( Table table, StringBuilder sb ) { + public String applyUniquesToTable( Table table ) { + // TODO: Am I correct that this shouldn't be done unless the constraint + // isn't created in an alter table? +// Iterator uniqueKeyIterator = table.getUniqueKeyIterator(); +// while ( uniqueKeyIterator.hasNext() ) { +// UniqueKey uniqueKey = (UniqueKey) uniqueKeyIterator.next(); +// +// sb.append( ", " ).append( createUniqueConstraint( uniqueKey) ); +// } + return ""; + } + + @Override + public String applyUniquesOnAlter( UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ) { // 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 ); -// } -// } +// return super.sqlCreateString( dialect, p, defaultCatalog, defaultSchema ); +// } +// else { +// return Index.buildSqlCreateIndexString( dialect, getName(), getTable(), getColumnIterator(), true, +// defaultCatalog, defaultSchema ); // } - Iterator ukiter = table.getUniqueKeyIterator(); - while ( ukiter.hasNext() ) { - UniqueKey uk = (UniqueKey) ukiter.next(); - String constraint = uk.sqlConstraintString( dialect ); - if ( constraint != null ) { - sb.append( ", " ).append( constraint ); + return new StringBuilder( "alter table " ) + .append( uniqueKey.getTable().getQualifiedName( + dialect, defaultCatalog, defaultSchema ) ) + .append( " add constraint " ) + .append( uniqueKey.getName() ) + .append( uniqueConstraintSql( uniqueKey ) ) + .toString(); + } + + @Override + public String dropUniquesOnAlter( UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ) { +// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { +// return super.sqlDropString( dialect, defaultCatalog, defaultSchema ); +// } +// else { +// return Index.buildSqlDropIndexString( dialect, getTable(), getName(), defaultCatalog, defaultSchema ); +// } + + return new StringBuilder( "alter table " ) + .append( uniqueKey.getTable().getQualifiedName( + dialect, defaultCatalog, defaultSchema ) ) + .append( " drop constraint " ) + .append( dialect.quote( uniqueKey.getName() ) ) + .toString(); + } + + @Override + public String uniqueConstraintSql( UniqueKey uniqueKey ) { + // TODO: This may not be necessary, but not all callers currently + // check it on their own. Go through their logic. +// if ( !isGenerated( dialect ) ) return null; + + StringBuilder sb = new StringBuilder(); + sb.append( " unique (" ); + Iterator columnIterator = uniqueKey.getColumnIterator(); + while ( columnIterator.hasNext() ) { + Column column = (Column) columnIterator.next(); + sb.append( column.getQuotedName( dialect ) ); + if ( columnIterator.hasNext() ) { + sb.append( ", " ); } } + + return sb.append( ')' ).toString(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index a173c22c8c..cbe2e19667 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -22,6 +22,7 @@ package org.hibernate.dialect.unique; import org.hibernate.mapping.Column; import org.hibernate.mapping.Table; +import org.hibernate.mapping.UniqueKey; /** * Dialect-level delegate in charge of applying "uniqueness" to a column. @@ -39,7 +40,57 @@ import org.hibernate.mapping.Table; */ public interface UniqueDelegate { - public void applyUnique( Table table, Column column, StringBuilder sb ); + /** + * If the delegate supports unique constraints, this method should simply + * create the UniqueKey on the Table. Otherwise, the constraint isn't + * supported and "unique" should be added to the column definition. + * + * @param table + * @param column + * @return String + */ + public String applyUniqueToColumn( Table table, Column column ); - public void createUniqueConstraint( Table table, StringBuilder sb ); + /** + * If creating unique constraints in separate alter statements are not + * supported, this method should return the syntax necessary to create + * the constraint on the original create table statement. + * + * @param table + * @return String + */ + public String applyUniquesToTable( Table table ); + + /** + * If creating unique constraints in separate alter statements is + * supported, generate the necessary "alter" syntax for the given key. + * + * @param uniqueKey + * @param defaultCatalog + * @param defaultSchema + * @return String + */ + public String applyUniquesOnAlter( UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ); + + /** + * If dropping unique constraints in separate alter statements is + * supported, generate the necessary "alter" syntax for the given key. + * + * @param uniqueKey + * @param defaultCatalog + * @param defaultSchema + * @return String + */ + public String dropUniquesOnAlter( UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ); + + /** + * Generates the syntax necessary to create the unique constraint (reused + * by all methods). Ex: "unique (column1, column2, ...)" + * + * @param uniqueKey + * @return String + */ + public String uniqueConstraintSql( UniqueKey uniqueKey ); } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 87c2148821..498793f29a 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -26,7 +26,6 @@ package org.hibernate.mapping; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -422,7 +421,9 @@ public class Table implements RelationalModel, Serializable { alter.append( " not null" ); } - dialect.getUniqueDelegate().applyUnique( this, column, alter ); + if ( column.isUnique() ) { + alter.append( dialect.getUniqueDelegate().applyUniqueToColumn( this, column ) ); + } if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) { alter.append( " check(" ) @@ -519,8 +520,10 @@ public class Table implements RelationalModel, Serializable { } - dialect.getUniqueDelegate().applyUnique( this, col, buf ); - + if ( col.isUnique() ) { + buf.append( dialect.getUniqueDelegate().applyUniqueToColumn( this, col ) ); + } + if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { buf.append( " check (" ) .append( col.getCheckConstraint() ) @@ -542,7 +545,7 @@ public class Table implements RelationalModel, Serializable { .append( getPrimaryKey().sqlConstraintString( dialect ) ); } - dialect.getUniqueDelegate().createUniqueConstraint( this, buf ); + buf.append( dialect.getUniqueDelegate().applyUniquesToTable( this ) ); if ( dialect.supportsTableCheck() ) { Iterator chiter = checkConstraints.iterator(); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java index 36cd559c6b..4368c7e293 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java @@ -22,89 +22,50 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.mapping; -import java.util.Iterator; - import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.Mapping; /** * A relational unique key constraint * - * @author Gavin King + * @author Brett Meyer */ public class UniqueKey extends Constraint { - public String sqlConstraintString(Dialect dialect) { - // TODO: This may not be necessary, but not all callers currently - // check it on their own. Go through their logic. - if ( !isGenerated( dialect ) ) return null; - - StringBuilder buf = new StringBuilder( "unique (" ); - Iterator iter = getColumnIterator(); - while ( iter.hasNext() ) { - Column column = (Column) iter.next(); - buf.append( column.getQuotedName( dialect ) ); - if ( iter.hasNext() ) { - buf.append( ", " ); - } - } - - return buf.append( ')' ).toString(); - } - @Override public String sqlConstraintString( Dialect dialect, String constraintName, String defaultCatalog, String defaultSchema) { - // TODO: This may not be necessary, but not all callers currently - // check it on their own. Go through their logic. - if ( !isGenerated( dialect ) ) return null; - - StringBuilder buf = new StringBuilder( - dialect.getAddUniqueConstraintString( constraintName ) ).append( '(' ); - Iterator iter = getColumnIterator(); - while ( iter.hasNext() ) { - Column column = (Column) iter.next(); - buf.append( column.getQuotedName( dialect ) ); - if ( iter.hasNext() ) buf.append( ", " ); - } - return buf.append( ')' ).toString(); + return dialect.getUniqueDelegate().uniqueConstraintSql( this ); } @Override - 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 sqlCreateString(Dialect dialect, Mapping p, + String defaultCatalog, String defaultSchema) { + return dialect.getUniqueDelegate().applyUniquesOnAlter( + this, defaultCatalog, defaultSchema ); } @Override - 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 String sqlDropString(Dialect dialect, String defaultCatalog, + String defaultSchema) { + return dialect.getUniqueDelegate().dropUniquesOnAlter( + this, defaultCatalog, defaultSchema ); } - @Override - public boolean isGenerated(Dialect dialect) { - if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return false; - if ( dialect.supportsNotNullUnique() ) return true; - - Iterator iter = getColumnIterator(); - while ( iter.hasNext() ) { - // Dialect does not support "not null unique" and this column is not null. - if ( ! ( (Column) iter.next() ).isNullable() ) return false; - } - return true; - } +// @Override +// public boolean isGenerated(Dialect dialect) { +// if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return false; +// if ( dialect.supportsNotNullUnique() ) return true; +// +// Iterator iter = getColumnIterator(); +// while ( iter.hasNext() ) { +// // Dialect does not support "not null unique" and this column is not null. +// if ( ! ( (Column) iter.next() ).isNullable() ) return false; +// } +// return true; +// } } From 7b05f4aed8845d4ccefce71eea438b81be10610e Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 12 Dec 2012 18:18:50 -0500 Subject: [PATCH 47/58] HHH-7797 Initial attempt at using UniqueDelegate within metamodel --- .../dialect/unique/DefaultUniqueDelegate.java | 110 ++++++++++++++++-- .../dialect/unique/UniqueDelegate.java | 59 +++++++++- .../java/org/hibernate/mapping/UniqueKey.java | 4 +- .../metamodel/relational/Database.java | 6 +- .../hibernate/metamodel/relational/Table.java | 25 +--- .../metamodel/relational/UniqueKey.java | 60 +++------- 6 files changed, 177 insertions(+), 87 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index a9c8ac33fc..5d9a34881e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -23,9 +23,9 @@ package org.hibernate.dialect.unique; import java.util.Iterator; import org.hibernate.dialect.Dialect; -import org.hibernate.mapping.Column; -import org.hibernate.mapping.Table; -import org.hibernate.mapping.UniqueKey; +import org.hibernate.metamodel.relational.Column; +import org.hibernate.metamodel.relational.Table; +import org.hibernate.metamodel.relational.UniqueKey; /** * The default UniqueDelegate implementation for most dialects. Uses @@ -41,6 +41,29 @@ public class DefaultUniqueDelegate implements UniqueDelegate { this.dialect = dialect; } + @Override + public String applyUniqueToColumn( org.hibernate.mapping.Table table, + org.hibernate.mapping.Column column ) { +// if ( column.isUnique() +// && ( column.isNullable() +// || dialect.supportsNotNullUnique() ) ) { +// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { +// // If the constraint is supported, do not add to the column syntax. +// UniqueKey uk = getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); +// uk.addColumn( column ); +// } +// else if ( dialect.supportsUnique() ) { +// // Otherwise, add to the column syntax if supported. +// sb.append( " unique" ); +// } +// } + + org.hibernate.mapping.UniqueKey uk = table.getOrCreateUniqueKey( + column.getQuotedName( dialect ) + '_' ); + uk.addColumn( column ); + return ""; + } + @Override public String applyUniqueToColumn( Table table, Column column ) { // if ( column.isUnique() @@ -57,12 +80,25 @@ public class DefaultUniqueDelegate implements UniqueDelegate { // } // } - UniqueKey uk = table.getOrCreateUniqueKey( - column.getQuotedName( dialect ) + '_' ); + UniqueKey uk = table.getOrCreateUniqueKey( column.getColumnName() + .encloseInQuotesIfQuoted( dialect ) + '_' ); uk.addColumn( column ); return ""; } + @Override + public String applyUniquesToTable( org.hibernate.mapping.Table table ) { + // TODO: Am I correct that this shouldn't be done unless the constraint + // isn't created in an alter table? +// Iterator uniqueKeyIterator = table.getUniqueKeyIterator(); +// while ( uniqueKeyIterator.hasNext() ) { +// UniqueKey uniqueKey = (UniqueKey) uniqueKeyIterator.next(); +// +// sb.append( ", " ).append( createUniqueConstraint( uniqueKey) ); +// } + return ""; + } + @Override public String applyUniquesToTable( Table table ) { // TODO: Am I correct that this shouldn't be done unless the constraint @@ -77,7 +113,7 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public String applyUniquesOnAlter( UniqueKey uniqueKey, + public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, String defaultCatalog, String defaultSchema ) { // if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { // return super.sqlCreateString( dialect, p, defaultCatalog, defaultSchema ); @@ -97,7 +133,25 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public String dropUniquesOnAlter( UniqueKey uniqueKey, + public String applyUniquesOnAlter( UniqueKey uniqueKey ) { +// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { +// return super.sqlCreateString( dialect, p, defaultCatalog, defaultSchema ); +// } +// else { +// return Index.buildSqlCreateIndexString( dialect, getName(), getTable(), getColumnIterator(), true, +// defaultCatalog, defaultSchema ); +// } + + return new StringBuilder( "alter table " ) + .append( uniqueKey.getTable().getQualifiedName( dialect ) ) + .append( " add constraint " ) + .append( uniqueKey.getName() ) + .append( uniqueConstraintSql( uniqueKey ) ) + .toString(); + } + + @Override + public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, String defaultCatalog, String defaultSchema ) { // if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { // return super.sqlDropString( dialect, defaultCatalog, defaultSchema ); @@ -115,7 +169,23 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public String uniqueConstraintSql( UniqueKey uniqueKey ) { + public String dropUniquesOnAlter( UniqueKey uniqueKey ) { +// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { +// return super.sqlDropString( dialect, defaultCatalog, defaultSchema ); +// } +// else { +// return Index.buildSqlDropIndexString( dialect, getTable(), getName(), defaultCatalog, defaultSchema ); +// } + + return new StringBuilder( "alter table " ) + .append( uniqueKey.getTable().getQualifiedName( dialect ) ) + .append( " drop constraint " ) + .append( dialect.quote( uniqueKey.getName() ) ) + .toString(); + } + + @Override + public String uniqueConstraintSql( org.hibernate.mapping.UniqueKey uniqueKey ) { // TODO: This may not be necessary, but not all callers currently // check it on their own. Go through their logic. // if ( !isGenerated( dialect ) ) return null; @@ -124,7 +194,29 @@ public class DefaultUniqueDelegate implements UniqueDelegate { sb.append( " unique (" ); Iterator columnIterator = uniqueKey.getColumnIterator(); while ( columnIterator.hasNext() ) { - Column column = (Column) columnIterator.next(); + org.hibernate.mapping.Column column + = (org.hibernate.mapping.Column) columnIterator.next(); + sb.append( column.getQuotedName( dialect ) ); + if ( columnIterator.hasNext() ) { + sb.append( ", " ); + } + } + + return sb.append( ')' ).toString(); + } + + @Override + public String uniqueConstraintSql( UniqueKey uniqueKey ) { + // TODO: This may not be necessary, but not all callers currently + // check it on their own. Go through their logic. +// if ( !isGenerated( dialect ) ) return null; + + StringBuilder sb = new StringBuilder(); + sb.append( " unique (" ); + Iterator columnIterator = uniqueKey.getColumns().iterator(); + while ( columnIterator.hasNext() ) { + org.hibernate.mapping.Column column + = (org.hibernate.mapping.Column) columnIterator.next(); sb.append( column.getQuotedName( dialect ) ); if ( columnIterator.hasNext() ) { sb.append( ", " ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index cbe2e19667..042f0c8c3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -20,9 +20,9 @@ */ package org.hibernate.dialect.unique; -import org.hibernate.mapping.Column; -import org.hibernate.mapping.Table; -import org.hibernate.mapping.UniqueKey; +import org.hibernate.metamodel.relational.Column; +import org.hibernate.metamodel.relational.Table; +import org.hibernate.metamodel.relational.UniqueKey; /** * Dialect-level delegate in charge of applying "uniqueness" to a column. @@ -40,6 +40,18 @@ import org.hibernate.mapping.UniqueKey; */ public interface UniqueDelegate { + /** + * If the delegate supports unique constraints, this method should simply + * create the UniqueKey on the Table. Otherwise, the constraint isn't + * supported and "unique" should be added to the column definition. + * + * @param table + * @param column + * @return String + */ + public String applyUniqueToColumn( org.hibernate.mapping.Table table, + org.hibernate.mapping.Column column ); + /** * If the delegate supports unique constraints, this method should simply * create the UniqueKey on the Table. Otherwise, the constraint isn't @@ -51,6 +63,16 @@ public interface UniqueDelegate { */ public String applyUniqueToColumn( Table table, Column column ); + /** + * If creating unique constraints in separate alter statements are not + * supported, this method should return the syntax necessary to create + * the constraint on the original create table statement. + * + * @param table + * @return String + */ + public String applyUniquesToTable( org.hibernate.mapping.Table table ); + /** * If creating unique constraints in separate alter statements are not * supported, this method should return the syntax necessary to create @@ -70,9 +92,18 @@ public interface UniqueDelegate { * @param defaultSchema * @return String */ - public String applyUniquesOnAlter( UniqueKey uniqueKey, + public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, String defaultCatalog, String defaultSchema ); + /** + * If creating unique constraints in separate alter statements is + * supported, generate the necessary "alter" syntax for the given key. + * + * @param uniqueKey + * @return String + */ + public String applyUniquesOnAlter( UniqueKey uniqueKey ); + /** * If dropping unique constraints in separate alter statements is * supported, generate the necessary "alter" syntax for the given key. @@ -82,9 +113,27 @@ public interface UniqueDelegate { * @param defaultSchema * @return String */ - public String dropUniquesOnAlter( UniqueKey uniqueKey, + public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, String defaultCatalog, String defaultSchema ); + /** + * If dropping unique constraints in separate alter statements is + * supported, generate the necessary "alter" syntax for the given key. + * + * @param uniqueKey + * @return String + */ + public String dropUniquesOnAlter( UniqueKey uniqueKey ); + + /** + * Generates the syntax necessary to create the unique constraint (reused + * by all methods). Ex: "unique (column1, column2, ...)" + * + * @param uniqueKey + * @return String + */ + public String uniqueConstraintSql( org.hibernate.mapping.UniqueKey uniqueKey ); + /** * Generates the syntax necessary to create the unique constraint (reused * by all methods). Ex: "unique (column1, column2, ...)" diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java index 4368c7e293..bf4bf07eda 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java @@ -38,7 +38,9 @@ public class UniqueKey extends Constraint { String constraintName, String defaultCatalog, String defaultSchema) { - return dialect.getUniqueDelegate().uniqueConstraintSql( this ); +// return dialect.getUniqueDelegate().uniqueConstraintSql( this ); + // Not used. + return ""; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Database.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Database.java index f15f565790..3291b967ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Database.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Database.java @@ -115,10 +115,8 @@ public class Database { for ( Schema schema : schemaMap.values() ) { for ( Table table : schema.getTables() ) { - if ( ! dialect.supportsUniqueConstraintInCreateAlterTable() ) { - for ( UniqueKey uniqueKey : table.getUniqueKeys() ) { - addSqlCreateStrings( dialect, exportIdentifiers, script, uniqueKey ); - } + for ( UniqueKey uniqueKey : table.getUniqueKeys() ) { + addSqlCreateStrings( dialect, exportIdentifiers, script, uniqueKey ); } for ( Index index : table.getIndexes() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 72ce0df0f2..1c70ac1cef 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -199,20 +199,8 @@ public class Table extends AbstractTableSpecification implements Exportable { } - // If the column is 1.) unique and nullable or 2.) unique, - // not null, and the dialect supports unique not null - if ( col.isUnique() - && ( col.isNullable() - || dialect.supportsNotNullUnique() ) ) { - if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { - // If the constraint is supported, do not add to the column syntax. - UniqueKey uk = getOrCreateUniqueKey( col.getColumnName().encloseInQuotesIfQuoted( dialect ) + '_' ); - uk.addColumn( col ); - } - else if ( dialect.supportsUnique() ) { - // Otherwise, add to the column syntax if supported. - buf.append( " unique" ); - } + if ( col.isUnique() ) { + buf.append( dialect.getUniqueDelegate().applyUniqueToColumn( this, col ) ); } if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) { @@ -231,14 +219,7 @@ public class Table extends AbstractTableSpecification implements Exportable { .append( getPrimaryKey().sqlConstraintStringInCreateTable( dialect ) ); } - if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { - for ( UniqueKey uk : uniqueKeys.values() ) { - String constraint = uk.sqlConstraintStringInCreateTable( dialect ); - if ( constraint != null ) { - buf.append( ", " ).append( constraint ); - } - } - } + buf.append( dialect.getUniqueDelegate().applyUniquesToTable( this ) ); if ( dialect.supportsTableCheck() ) { for ( CheckConstraint checkConstraint : checkConstraints ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java index 1c5413e473..339ba1d20e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/UniqueKey.java @@ -47,54 +47,22 @@ public class UniqueKey extends AbstractConstraint implements Constraint { } @Override - public boolean isCreationVetoed(Dialect dialect) { - if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return true; - if ( dialect.supportsNotNullUnique() ) return false; - - for ( Column column : getColumns() ) { - // Dialect does not support "not null unique" and this column is not null. - if ( ! column.isNullable() ) return true; - } - return false; - } - - public String sqlConstraintStringInCreateTable(Dialect dialect) { - // TODO: This may not be necessary, but not all callers currently - // check it on their own. Go through their logic. - if ( isCreationVetoed( dialect ) ) return null; - - StringBuilder buf = new StringBuilder( "unique (" ); - boolean first = true; - for ( Column column : getColumns() ) { - if ( first ) { - first = false; - } - else { - buf.append( ", " ); - } - buf.append( column.getColumnName().encloseInQuotesIfQuoted( dialect ) ); - } - return buf.append( ')' ).toString(); + public String[] sqlCreateStrings(Dialect dialect) { + return new String[] { + dialect.getUniqueDelegate().applyUniquesOnAlter( this ) + }; } @Override - public String sqlConstraintStringInAlterTable(Dialect dialect) { - // TODO: This may not be necessary, but not all callers currently - // check it on their own. Go through their logic. - if ( isCreationVetoed( dialect ) ) return null; - - StringBuilder buf = new StringBuilder( - dialect.getAddUniqueConstraintString( getName() ) ).append( '(' ); - boolean first = true; - for ( Column column : getColumns() ) { - if ( first ) { - first = false; - } - else { - buf.append( ", " ); - } - buf.append( column.getColumnName().encloseInQuotesIfQuoted( dialect ) ); - } - return buf.append( ')' ).toString(); + public String[] sqlDropStrings(Dialect dialect) { + return new String[] { + dialect.getUniqueDelegate().dropUniquesOnAlter( this ) + }; + } + + @Override + protected String sqlConstraintStringInAlterTable(Dialect dialect) { + // not used + return ""; } } From 962d1e580dce2f36c5aef824d1439e79e878000c Mon Sep 17 00:00:00 2001 From: brmeyer Date: Tue, 11 Dec 2012 15:21:35 -0500 Subject: [PATCH 48/58] HHH-7797 initial test case --- .../UniqueConstraintTest.java | 8 +-- .../test/constraint/ConstraintTest.java | 64 +++++++++++++++++++ .../org/hibernate/testing/DialectChecks.java | 6 -- 3 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/uniqueconstraint/UniqueConstraintTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/uniqueconstraint/UniqueConstraintTest.java index c6335c9988..08048f050b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/uniqueconstraint/UniqueConstraintTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/uniqueconstraint/UniqueConstraintTest.java @@ -1,15 +1,12 @@ package org.hibernate.test.annotations.uniqueconstraint; -import org.junit.Test; +import static org.junit.Assert.fail; import org.hibernate.JDBCException; import org.hibernate.Session; import org.hibernate.Transaction; -import org.hibernate.testing.DialectChecks; -import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; - -import static org.junit.Assert.fail; +import org.junit.Test; /** * @author Manuel Bernhardt @@ -25,7 +22,6 @@ public class UniqueConstraintTest extends BaseCoreFunctionalTestCase { } @Test - @RequiresDialectFeature( DialectChecks.SupportNotNullUnique.class ) public void testUniquenessConstraintWithSuperclassProperty() throws Exception { Session s = openSession(); Transaction tx = s.beginTransaction(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java b/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java new file mode 100644 index 0000000000..936f61c73c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java @@ -0,0 +1,64 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.test.constraint; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +/** + * HHH-7797 re-wrote the way dialects handle unique constraints. Test as + * many variations of unique, not null, and primary key constraints as possible. + * + * @author Brett Meyer + */ +@TestForIssue( jiraKey = "HHH-7797" ) +public class ConstraintTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Entity1.class + }; + } + + @Test + public void testConstraints() { + // nothing yet -- more interested in DDL creation + } + + // Primary key w/ not null and unique + @Entity + public static class Entity1 { + @Id + @GeneratedValue +// @Column( nullable = false, unique = true) + public long id; + + @Column( nullable = false, unique = true) + public String foo; + } +} \ No newline at end of file diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index a2e56fdda2..8408818ca0 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -92,12 +92,6 @@ abstract public class DialectChecks { } } - public static class SupportNotNullUnique implements DialectCheck { - public boolean isMatch(Dialect dialect) { - return dialect.supportsNotNullUnique(); - } - } - public static class SupportLimitCheck implements DialectCheck { public boolean isMatch(Dialect dialect) { return dialect.supportsLimit(); From 49c8a8e4f07c00973ca8f98e8bdd05a7392991e5 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Fri, 14 Dec 2012 11:45:35 -0500 Subject: [PATCH 49/58] HHH-7797 Finished auditing dialects. Cleanup and javadocs. Completed uniqueness test. --- .../org/hibernate/dialect/Cache71Dialect.java | 5 - .../java/org/hibernate/dialect/H2Dialect.java | 4 - .../org/hibernate/dialect/HSQLDialect.java | 4 - .../org/hibernate/dialect/IngresDialect.java | 7 -- .../hibernate/dialect/RDMSOS2200Dialect.java | 7 -- .../hibernate/dialect/SybaseASE15Dialect.java | 4 - .../hibernate/dialect/TimesTenDialect.java | 8 -- .../dialect/unique/DefaultUniqueDelegate.java | 92 ++----------------- .../dialect/unique/UniqueDelegate.java | 24 +++-- .../test/constraint/ConstraintTest.java | 37 ++++++-- 10 files changed, 53 insertions(+), 139 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java index 702b721531..c2811e3b42 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java @@ -401,11 +401,6 @@ public class Cache71Dialect extends Dialect { 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. * diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index e4bc9bdfc8..9adc838f0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -216,10 +216,6 @@ public class H2Dialect extends Dialect { return " for update"; } - public boolean supportsUnique() { - return true; - } - public boolean supportsLimit() { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index ca64af3dcd..6c387e6c9e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -251,10 +251,6 @@ public class HSQLDialect extends Dialect { } } - public boolean supportsUnique() { - return false; - } - public boolean supportsLimit() { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java index a7dbaf194c..e5b9f8a20e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java @@ -306,13 +306,6 @@ public class IngresDialect extends Dialect { return true; } - /** - * Ingres explicitly needs "unique not null", because "with null" is default - */ - public boolean supportsNotNullUnique() { - return false; - } - /** * Does this dialect support temporary tables? */ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java index 6d45c1c6d5..ccec37a7c7 100755 --- a/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java @@ -240,13 +240,6 @@ public class RDMSOS2200Dialect extends Dialect { 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. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java index 423ced7cb7..01f8d25776 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java @@ -422,10 +422,6 @@ public class SybaseASE15Dialect extends SybaseDialect { return false; } - public boolean supportsUniqueConstraintInCreateAlterTable() { - return false; - } - public String getCrossJoinSeparator() { return ", "; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java index b2f611aeb2..f76e3099a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java @@ -102,14 +102,6 @@ public class TimesTenDialect extends Dialect { public boolean qualifyIndexName() { return false; } - - public boolean supportsUnique() { - return false; - } - - public boolean supportsUniqueConstraintInCreateAlterTable() { - return false; - } public String getAddColumnString() { return "add"; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index 5d9a34881e..6c872434b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -44,20 +44,6 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String applyUniqueToColumn( org.hibernate.mapping.Table table, org.hibernate.mapping.Column column ) { -// if ( column.isUnique() -// && ( column.isNullable() -// || dialect.supportsNotNullUnique() ) ) { -// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { -// // If the constraint is supported, do not add to the column syntax. -// UniqueKey uk = getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); -// uk.addColumn( column ); -// } -// else if ( dialect.supportsUnique() ) { -// // Otherwise, add to the column syntax if supported. -// sb.append( " unique" ); -// } -// } - org.hibernate.mapping.UniqueKey uk = table.getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); uk.addColumn( column ); @@ -66,20 +52,6 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String applyUniqueToColumn( Table table, Column column ) { -// if ( column.isUnique() -// && ( column.isNullable() -// || dialect.supportsNotNullUnique() ) ) { -// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { -// // If the constraint is supported, do not add to the column syntax. -// UniqueKey uk = getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); -// uk.addColumn( column ); -// } -// else if ( dialect.supportsUnique() ) { -// // Otherwise, add to the column syntax if supported. -// sb.append( " unique" ); -// } -// } - UniqueKey uk = table.getOrCreateUniqueKey( column.getColumnName() .encloseInQuotesIfQuoted( dialect ) + '_' ); uk.addColumn( column ); @@ -88,41 +60,19 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String applyUniquesToTable( org.hibernate.mapping.Table table ) { - // TODO: Am I correct that this shouldn't be done unless the constraint - // isn't created in an alter table? -// Iterator uniqueKeyIterator = table.getUniqueKeyIterator(); -// while ( uniqueKeyIterator.hasNext() ) { -// UniqueKey uniqueKey = (UniqueKey) uniqueKeyIterator.next(); -// -// sb.append( ", " ).append( createUniqueConstraint( uniqueKey) ); -// } return ""; } @Override public String applyUniquesToTable( Table table ) { - // TODO: Am I correct that this shouldn't be done unless the constraint - // isn't created in an alter table? -// Iterator uniqueKeyIterator = table.getUniqueKeyIterator(); -// while ( uniqueKeyIterator.hasNext() ) { -// UniqueKey uniqueKey = (UniqueKey) uniqueKeyIterator.next(); -// -// sb.append( ", " ).append( createUniqueConstraint( uniqueKey) ); -// } return ""; } @Override public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, 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 ); -// } - + // Do this here, rather than allowing UniqueKey/Constraint to do it. + // We need full, simplified control over whether or not it happens. return new StringBuilder( "alter table " ) .append( uniqueKey.getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ) ) @@ -134,15 +84,9 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String applyUniquesOnAlter( UniqueKey uniqueKey ) { -// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { -// return super.sqlCreateString( dialect, p, defaultCatalog, defaultSchema ); -// } -// else { -// return Index.buildSqlCreateIndexString( dialect, getName(), getTable(), getColumnIterator(), true, -// defaultCatalog, defaultSchema ); -// } - - return new StringBuilder( "alter table " ) + // Do this here, rather than allowing UniqueKey/Constraint to do it. + // We need full, simplified control over whether or not it happens. + return new StringBuilder( "alter table " ) .append( uniqueKey.getTable().getQualifiedName( dialect ) ) .append( " add constraint " ) .append( uniqueKey.getName() ) @@ -153,13 +97,8 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, String defaultCatalog, String defaultSchema ) { -// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { -// return super.sqlDropString( dialect, defaultCatalog, defaultSchema ); -// } -// else { -// return Index.buildSqlDropIndexString( dialect, getTable(), getName(), defaultCatalog, defaultSchema ); -// } - + // Do this here, rather than allowing UniqueKey/Constraint to do it. + // We need full, simplified control over whether or not it happens. return new StringBuilder( "alter table " ) .append( uniqueKey.getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema ) ) @@ -170,13 +109,8 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String dropUniquesOnAlter( UniqueKey uniqueKey ) { -// if ( dialect.supportsUniqueConstraintInCreateAlterTable() ) { -// return super.sqlDropString( dialect, defaultCatalog, defaultSchema ); -// } -// else { -// return Index.buildSqlDropIndexString( dialect, getTable(), getName(), defaultCatalog, defaultSchema ); -// } - + // Do this here, rather than allowing UniqueKey/Constraint to do it. + // We need full, simplified control over whether or not it happens. return new StringBuilder( "alter table " ) .append( uniqueKey.getTable().getQualifiedName( dialect ) ) .append( " drop constraint " ) @@ -186,10 +120,6 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String uniqueConstraintSql( org.hibernate.mapping.UniqueKey uniqueKey ) { - // TODO: This may not be necessary, but not all callers currently - // check it on their own. Go through their logic. -// if ( !isGenerated( dialect ) ) return null; - StringBuilder sb = new StringBuilder(); sb.append( " unique (" ); Iterator columnIterator = uniqueKey.getColumnIterator(); @@ -207,10 +137,6 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String uniqueConstraintSql( UniqueKey uniqueKey ) { - // TODO: This may not be necessary, but not all callers currently - // check it on their own. Go through their logic. -// if ( !isGenerated( dialect ) ) return null; - StringBuilder sb = new StringBuilder(); sb.append( " unique (" ); Iterator columnIterator = uniqueKey.getColumns().iterator(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index 042f0c8c3c..d5ff514649 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -28,12 +28,16 @@ import org.hibernate.metamodel.relational.UniqueKey; * Dialect-level delegate in charge of applying "uniqueness" to a column. * Uniqueness can be defined in 1 of 3 ways: * - * 1.) Add a unique constraint via separate create/alter table statements. + * 1.) Add a unique constraint via separate alter table statements. * 2.) Add a unique constraint via dialect-specific syntax in table create statement. * 3.) Add "unique" syntax to the column itself. * * #1 & #2 are preferred, if possible -- #3 should be solely a fall-back. * + * TODO: This could eventually be simplified. With AST, 1 "applyUniqueness" + * method might be possible. But due to .cfg and .mapping still resolving + * around StringBuilders, separate methods were needed. + * * See HHH-7797. * * @author Brett Meyer @@ -43,7 +47,8 @@ public interface UniqueDelegate { /** * If the delegate supports unique constraints, this method should simply * create the UniqueKey on the Table. Otherwise, the constraint isn't - * supported and "unique" should be added to the column definition. + * supported and "unique" should be returned in order to add it + * to the column definition. * * @param table * @param column @@ -55,7 +60,8 @@ public interface UniqueDelegate { /** * If the delegate supports unique constraints, this method should simply * create the UniqueKey on the Table. Otherwise, the constraint isn't - * supported and "unique" should be added to the column definition. + * supported and "unique" should be returned in order to add it + * to the column definition. * * @param table * @param column @@ -64,9 +70,9 @@ public interface UniqueDelegate { public String applyUniqueToColumn( Table table, Column column ); /** - * If creating unique constraints in separate alter statements are not - * supported, this method should return the syntax necessary to create - * the constraint on the original create table statement. + * If constraints are supported, but not in seperate alter statements, + * return uniqueConstraintSql in order to add the constraint to the + * original table definition. * * @param table * @return String @@ -74,9 +80,9 @@ public interface UniqueDelegate { public String applyUniquesToTable( org.hibernate.mapping.Table table ); /** - * If creating unique constraints in separate alter statements are not - * supported, this method should return the syntax necessary to create - * the constraint on the original create table statement. + * If constraints are supported, but not in seperate alter statements, + * return uniqueConstraintSql in order to add the constraint to the + * original table definition. * * @param table * @return String diff --git a/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java b/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java index 936f61c73c..5685d986d8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/constraint/ConstraintTest.java @@ -20,18 +20,23 @@ */ package org.hibernate.test.constraint; -import javax.persistence.Column; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Table; +import org.hibernate.mapping.Column; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; /** - * HHH-7797 re-wrote the way dialects handle unique constraints. Test as - * many variations of unique, not null, and primary key constraints as possible. + * HHH-7797 re-wrote the way dialects handle unique constraints. Test + * variations of unique & not null to ensure the constraints are created + * correctly for each dialect. * * @author Brett Meyer */ @@ -47,18 +52,34 @@ public class ConstraintTest extends BaseCoreFunctionalTestCase { @Test public void testConstraints() { - // nothing yet -- more interested in DDL creation + Column column = (Column) configuration().getClassMapping( Entity1.class.getName() ) + .getProperty( "foo1" ).getColumnIterator().next(); + assertFalse( column.isNullable() ); + assertTrue( column.isUnique() ); + + column = (Column) configuration().getClassMapping( Entity1.class.getName() ) + .getProperty( "foo2" ).getColumnIterator().next(); + assertTrue( column.isNullable() ); + assertTrue( column.isUnique() ); + + column = (Column) configuration().getClassMapping( Entity1.class.getName() ) + .getProperty( "id" ).getColumnIterator().next(); + assertFalse( column.isNullable() ); + assertTrue( column.isUnique() ); } - // Primary key w/ not null and unique @Entity + @Table( name = "Entity1" ) public static class Entity1 { @Id @GeneratedValue -// @Column( nullable = false, unique = true) + @javax.persistence.Column( nullable = false, unique = true) public long id; - @Column( nullable = false, unique = true) - public String foo; + @javax.persistence.Column( nullable = false, unique = true) + public String foo1; + + @javax.persistence.Column( nullable = true, unique = true) + public String foo2; } } \ No newline at end of file From 9bec5d12ff248896d43abb15f8c8e097aafb6686 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Fri, 14 Dec 2012 11:58:09 -0500 Subject: [PATCH 50/58] HHH-7797 minor refactoring --- .../dialect/unique/DefaultUniqueDelegate.java | 13 ++++++---- .../dialect/unique/UniqueDelegate.java | 25 +++++++++++-------- .../java/org/hibernate/mapping/Table.java | 9 ++++--- .../hibernate/metamodel/relational/Table.java | 3 ++- 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index 6c872434b7..aabdcd57a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -42,19 +42,22 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public String applyUniqueToColumn( org.hibernate.mapping.Table table, + public void generateUniqueKey( org.hibernate.mapping.Table table, org.hibernate.mapping.Column column ) { org.hibernate.mapping.UniqueKey uk = table.getOrCreateUniqueKey( column.getQuotedName( dialect ) + '_' ); uk.addColumn( column ); - return ""; } @Override - public String applyUniqueToColumn( Table table, Column column ) { + public void generateUniqueKey( Table table, Column column ) { UniqueKey uk = table.getOrCreateUniqueKey( column.getColumnName() .encloseInQuotesIfQuoted( dialect ) + '_' ); uk.addColumn( column ); + } + + @Override + public String applyUniqueToColumn() { return ""; } @@ -85,8 +88,8 @@ public class DefaultUniqueDelegate implements UniqueDelegate { @Override public String applyUniquesOnAlter( UniqueKey uniqueKey ) { // Do this here, rather than allowing UniqueKey/Constraint to do it. - // We need full, simplified control over whether or not it happens. - return new StringBuilder( "alter table " ) + // We need full, simplified control over whether or not it happens. + return new StringBuilder( "alter table " ) .append( uniqueKey.getTable().getQualifiedName( dialect ) ) .append( " add constraint " ) .append( uniqueKey.getName() ) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index d5ff514649..fc448645a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -45,29 +45,32 @@ import org.hibernate.metamodel.relational.UniqueKey; public interface UniqueDelegate { /** - * If the delegate supports unique constraints, this method should simply - * create the UniqueKey on the Table. Otherwise, the constraint isn't - * supported and "unique" should be returned in order to add it - * to the column definition. + * If the dialect supports unique constraints, create and add a UniqueKey + * to the Table. * * @param table * @param column - * @return String */ - public String applyUniqueToColumn( org.hibernate.mapping.Table table, + public void generateUniqueKey( org.hibernate.mapping.Table table, org.hibernate.mapping.Column column ); /** - * If the delegate supports unique constraints, this method should simply - * create the UniqueKey on the Table. Otherwise, the constraint isn't - * supported and "unique" should be returned in order to add it - * to the column definition. + * If the dialect supports unique constraints, create and add a UniqueKey + * to the Table. * * @param table * @param column + */ + public void generateUniqueKey( Table table, Column column ); + + /** + * If the dialect does not supports unique constraints, this method should + * return the syntax necessary to mutate the column definition + * (usually "unique"). + * * @return String */ - public String applyUniqueToColumn( Table table, Column column ); + public String applyUniqueToColumn(); /** * If constraints are supported, but not in seperate alter statements, diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 498793f29a..af49653db4 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -422,7 +422,10 @@ public class Table implements RelationalModel, Serializable { } if ( column.isUnique() ) { - alter.append( dialect.getUniqueDelegate().applyUniqueToColumn( this, column ) ); + dialect.getUniqueDelegate().generateUniqueKey( + this, column ); + alter.append( + dialect.getUniqueDelegate().applyUniqueToColumn() ); } if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) { @@ -521,8 +524,8 @@ public class Table implements RelationalModel, Serializable { } if ( col.isUnique() ) { - buf.append( dialect.getUniqueDelegate().applyUniqueToColumn( this, col ) ); - } + dialect.getUniqueDelegate().generateUniqueKey( this, col ); + buf.append( dialect.getUniqueDelegate().applyUniqueToColumn() ); } if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { buf.append( " check (" ) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 1c70ac1cef..76f12e77d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -200,7 +200,8 @@ public class Table extends AbstractTableSpecification implements Exportable { } if ( col.isUnique() ) { - buf.append( dialect.getUniqueDelegate().applyUniqueToColumn( this, col ) ); + dialect.getUniqueDelegate().generateUniqueKey( this, col ); + buf.append( dialect.getUniqueDelegate().applyUniqueToColumn() ); } if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) { From b4e8c5a83bb1ef0c935e2b5f7242017ab19524e8 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 17 Dec 2012 10:58:04 -0600 Subject: [PATCH 51/58] test asserting Hibernate handling of unclear jpa behavior wrt merging detached object with reference to transient object via non-cascaded association --- ...thTransientNonCascadedAssociationTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cascade/MergeWithTransientNonCascadedAssociationTest.java diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cascade/MergeWithTransientNonCascadedAssociationTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cascade/MergeWithTransientNonCascadedAssociationTest.java new file mode 100644 index 0000000000..a64855ad3f --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cascade/MergeWithTransientNonCascadedAssociationTest.java @@ -0,0 +1,103 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.cascade; + +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +import org.hibernate.LockMode; +import org.hibernate.Session; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.Test; + +import static org.junit.Assert.fail; + +/** + * @author Steve Ebersole + */ +public class MergeWithTransientNonCascadedAssociationTest extends BaseEntityManagerFunctionalTestCase { + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { Person.class, Address.class }; + } + + @Test + public void testMergeWithTransientNonCascadedAssociation() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + Person person = new Person(); + em.persist( person ); + em.getTransaction().commit(); + em.close(); + + person.address = new Address(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.merge( person ); + try { + em.flush(); + fail( "Expecting IllegalStateException" ); + } + catch (IllegalStateException ise) { + // expected... + em.getTransaction().rollback(); + } + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + person.address = null; + em.unwrap( Session.class ).lock( person, LockMode.NONE ); + em.unwrap( Session.class ).delete( person ); + em.getTransaction().commit(); + em.close(); + } + + @Entity( name = "Person" ) + public static class Person { + @Id + @GeneratedValue( generator = "increment" ) + @GenericGenerator( name = "increment", strategy = "increment" ) + private Integer id; + @ManyToOne + private Address address; + + public Person() { + } + } + + @Entity( name = "Address" ) + public static class Address { + @Id + @GeneratedValue( generator = "increment" ) + @GenericGenerator( name = "increment", strategy = "increment" ) + private Integer id; + } +} From 41397f22d17d165dc03e291a81a172dcaed232d5 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Mon, 17 Dec 2012 13:48:12 -0500 Subject: [PATCH 52/58] HHH-7797 Use unique indexes on nullable columns for DB2. Correctly handle @UniqueConstraint table annotations on second passes. --- .../java/org/hibernate/cfg/Configuration.java | 7 +- .../org/hibernate/dialect/DB2Dialect.java | 11 ++ .../dialect/unique/DB2UniqueDelegate.java | 109 ++++++++++++++++++ .../dialect/unique/DefaultUniqueDelegate.java | 9 +- .../dialect/unique/UniqueDelegate.java | 13 ++- .../java/org/hibernate/mapping/Index.java | 1 - .../java/org/hibernate/mapping/Table.java | 8 +- .../java/org/hibernate/mapping/UniqueKey.java | 13 --- .../hibernate/metamodel/relational/Index.java | 13 ++- .../hibernate/metamodel/relational/Table.java | 3 +- 10 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index f5e59e02f2..48dfaf6e1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -1544,7 +1544,6 @@ public class Configuration implements Serializable { private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames) { keyName = normalizer.normalizeIdentifierQuoting( keyName ); - UniqueKey uc; int size = columnNames.length; Column[] columns = new Column[size]; Set unbound = new HashSet(); @@ -1563,8 +1562,10 @@ public class Configuration implements Serializable { } for ( Column column : columns ) { if ( table.containsColumn( column ) ) { - uc = table.getOrCreateUniqueKey( keyName ); - uc.addColumn( table.getColumn( column ) ); + Column tableColumn = table.getColumn( column ); + tableColumn.setUnique( true ); + Dialect.getDialect().getUniqueDelegate().generateUniqueKey( + table, tableColumn ); unbound.remove( column ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 01f13b7883..61f1576f10 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -35,6 +35,8 @@ 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.unique.DB2UniqueDelegate; +import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.internal.util.JdbcExceptionHelper; @@ -48,6 +50,8 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; * @author Gavin King */ public class DB2Dialect extends Dialect { + + private final UniqueDelegate uniqueDelegate; public DB2Dialect() { super(); @@ -174,6 +178,8 @@ public class DB2Dialect extends Dialect { registerKeyword( "only" ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH ); + + uniqueDelegate = new DB2UniqueDelegate( this ); } @Override public String getLowercaseFunction() { @@ -451,5 +457,10 @@ public class DB2Dialect extends Dialect { } }; } + + @Override + public UniqueDelegate getUniqueDelegate() { + return uniqueDelegate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java new file mode 100644 index 0000000000..852b7c7854 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DB2UniqueDelegate.java @@ -0,0 +1,109 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +package org.hibernate.dialect.unique; + +import java.util.Iterator; + +import org.hibernate.dialect.Dialect; +import org.hibernate.metamodel.relational.Column; +import org.hibernate.metamodel.relational.Index; +import org.hibernate.metamodel.relational.UniqueKey; + +/** + * DB2 does not allow unique constraints on nullable columns. Rather than + * forcing "not null", use unique *indexes* instead. + * + * @author Brett Meyer + */ +public class DB2UniqueDelegate extends DefaultUniqueDelegate { + + public DB2UniqueDelegate( Dialect dialect ) { + super( dialect ); + } + + @Override + public String applyUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ) { + if ( hasNullable( uniqueKey ) ) { + return org.hibernate.mapping.Index.buildSqlCreateIndexString( + dialect, uniqueKey.getName(), uniqueKey.getTable(), + uniqueKey.columnIterator(), true, defaultCatalog, + defaultSchema ); + } else { + return super.applyUniquesOnAlter( + uniqueKey, defaultCatalog, defaultSchema ); + } + } + + @Override + public String applyUniquesOnAlter( UniqueKey uniqueKey ) { + if ( hasNullable( uniqueKey ) ) { + return Index.buildSqlCreateIndexString( + dialect, uniqueKey.getName(), uniqueKey.getTable(), + uniqueKey.getColumns(), true ); + } else { + return super.applyUniquesOnAlter( uniqueKey ); + } + } + + @Override + public String dropUniquesOnAlter( org.hibernate.mapping.UniqueKey uniqueKey, + String defaultCatalog, String defaultSchema ) { + if ( hasNullable( uniqueKey ) ) { + return org.hibernate.mapping.Index.buildSqlDropIndexString( + dialect, uniqueKey.getTable(), uniqueKey.getName(), + defaultCatalog, defaultSchema ); + } else { + return super.dropUniquesOnAlter( + uniqueKey, defaultCatalog, defaultSchema ); + } + } + + @Override + public String dropUniquesOnAlter( UniqueKey uniqueKey ) { + if ( hasNullable( uniqueKey ) ) { + return Index.buildSqlDropIndexString( + dialect, uniqueKey.getTable(), uniqueKey.getName() ); + } else { + return super.dropUniquesOnAlter( uniqueKey ); + } + } + + private boolean hasNullable( org.hibernate.mapping.UniqueKey uniqueKey ) { + Iterator iter = uniqueKey.getColumnIterator(); + while ( iter.hasNext() ) { + if ( ( ( org.hibernate.mapping.Column ) iter.next() ).isNullable() ) { + return true; + } + } + return false; + } + + private boolean hasNullable( UniqueKey uniqueKey ) { + Iterator iter = uniqueKey.getColumns().iterator(); + while ( iter.hasNext() ) { + if ( ( ( Column ) iter.next() ).isNullable() ) { + return true; + } + } + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index aabdcd57a1..41678aa5b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -35,7 +35,7 @@ import org.hibernate.metamodel.relational.UniqueKey; */ public class DefaultUniqueDelegate implements UniqueDelegate { - private final Dialect dialect; + protected final Dialect dialect; public DefaultUniqueDelegate( Dialect dialect ) { this.dialect = dialect; @@ -57,7 +57,12 @@ public class DefaultUniqueDelegate implements UniqueDelegate { } @Override - public String applyUniqueToColumn() { + public String applyUniqueToColumn( org.hibernate.mapping.Column column ) { + return ""; + } + + @Override + public String applyUniqueToColumn( Column column ) { return ""; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index fc448645a4..ef3728361d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -68,9 +68,20 @@ public interface UniqueDelegate { * return the syntax necessary to mutate the column definition * (usually "unique"). * + * @param column * @return String */ - public String applyUniqueToColumn(); + public String applyUniqueToColumn( org.hibernate.mapping.Column column ); + + /** + * If the dialect does not supports unique constraints, this method should + * return the syntax necessary to mutate the column definition + * (usually "unique"). + * + * @param column + * @return String + */ + public String applyUniqueToColumn( Column column ); /** * If constraints are supported, but not in seperate alter statements, diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Index.java b/hibernate-core/src/main/java/org/hibernate/mapping/Index.java index f91fc55694..da70576a05 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Index.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Index.java @@ -79,7 +79,6 @@ public class Index implements RelationalModel, Serializable { String defaultCatalog, String defaultSchema ) { - //TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far StringBuilder buf = new StringBuilder( "create" ) .append( unique ? " unique" : diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index af49653db4..8aaf8e0064 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -424,8 +424,8 @@ public class Table implements RelationalModel, Serializable { if ( column.isUnique() ) { dialect.getUniqueDelegate().generateUniqueKey( this, column ); - alter.append( - dialect.getUniqueDelegate().applyUniqueToColumn() ); + alter.append( dialect.getUniqueDelegate() + .applyUniqueToColumn( column ) ); } if ( column.hasCheckConstraint() && dialect.supportsColumnCheck() ) { @@ -525,7 +525,9 @@ public class Table implements RelationalModel, Serializable { if ( col.isUnique() ) { dialect.getUniqueDelegate().generateUniqueKey( this, col ); - buf.append( dialect.getUniqueDelegate().applyUniqueToColumn() ); } + buf.append( dialect.getUniqueDelegate() + .applyUniqueToColumn( col ) ); + } if ( col.hasCheckConstraint() && dialect.supportsColumnCheck() ) { buf.append( " check (" ) diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java index bf4bf07eda..e5613817b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/UniqueKey.java @@ -57,17 +57,4 @@ public class UniqueKey extends Constraint { this, defaultCatalog, defaultSchema ); } -// @Override -// public boolean isGenerated(Dialect dialect) { -// if ( !dialect.supportsUniqueConstraintInCreateAlterTable() ) return false; -// if ( dialect.supportsNotNullUnique() ) return true; -// -// Iterator iter = getColumnIterator(); -// while ( iter.hasNext() ) { -// // Dialect does not support "not null unique" and this column is not null. -// if ( ! ( (Column) iter.next() ).isNullable() ) return false; -// } -// return true; -// } - } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java index 20b4356ebc..2eb119c914 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Index.java @@ -63,7 +63,6 @@ public class Index extends AbstractConstraint implements Constraint { Iterable columns, boolean unique ) { - //TODO handle supportsNotNullUnique=false, but such a case does not exist in the wild so far StringBuilder buf = new StringBuilder( "create" ) .append( unique ? " unique" : @@ -89,6 +88,18 @@ public class Index extends AbstractConstraint implements Constraint { return buf.toString(); } + public static String buildSqlDropIndexString( + Dialect dialect, + TableSpecification table, + String name + ) { + return "drop index " + + StringHelper.qualify( + table.getQualifiedName( dialect ), + name + ); + } + public String sqlConstraintStringInAlterTable(Dialect dialect) { StringBuilder buf = new StringBuilder( " index (" ); boolean first = true; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 76f12e77d8..69cdb3cf0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -201,7 +201,8 @@ public class Table extends AbstractTableSpecification implements Exportable { if ( col.isUnique() ) { dialect.getUniqueDelegate().generateUniqueKey( this, col ); - buf.append( dialect.getUniqueDelegate().applyUniqueToColumn() ); + buf.append( dialect.getUniqueDelegate() + .applyUniqueToColumn( col ) ); } if ( col.getCheckCondition() != null && dialect.supportsColumnCheck() ) { From 1d5bf1608e977dc861f4b6d4e299836fa0d23783 Mon Sep 17 00:00:00 2001 From: brmeyer Date: Mon, 17 Dec 2012 14:10:05 -0500 Subject: [PATCH 53/58] HHH-7797 Corrected failing test that was exposed by src changes --- .../test/annotations/EntityTest.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java index 9fbdfa7187..877461835c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java @@ -190,6 +190,7 @@ public class EntityTest extends BaseCoreFunctionalTestCase { int id = 5; Session s; Transaction tx; + s = openSession(); tx = s.beginTransaction(); Sky sky = new Sky(); @@ -197,6 +198,9 @@ public class EntityTest extends BaseCoreFunctionalTestCase { sky.color = "green"; sky.day = "monday"; sky.month = "March"; + s.persist( sky ); + tx.commit(); + s.close(); Sky otherSky = new Sky(); otherSky.id = new Long( id++ ); @@ -210,17 +214,25 @@ public class EntityTest extends BaseCoreFunctionalTestCase { sameSky.day = "monday"; sameSky.month = "March"; - s.save( sky ); - s.flush(); - - s.save( otherSky ); - tx.commit(); - s.close(); + s = openSession(); + tx = s.beginTransaction(); + try { + s.persist( otherSky ); + tx.commit(); + fail( "unique constraints not respected" ); + } + catch (HibernateException e) { + //success + } + finally { + if ( tx != null ) tx.rollback(); + s.close(); + } s = openSession(); tx = s.beginTransaction(); try { - s.save( sameSky ); + s.persist( sameSky ); tx.commit(); fail( "unique constraints not respected" ); } From b0efdb4d1473f9c4d76dc6f208e3f2ee10cfe0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarre=C3=B1o?= Date: Tue, 18 Dec 2012 09:17:15 +0100 Subject: [PATCH 54/58] HHH-7871 Write cache should not using LOCAL flag * Flags were not being applied in clustered environments because of the use of classloader aware cache which was not forwarding them. --- .../cache/infinispan/InfinispanRegionFactory.java | 5 ----- .../access/TransactionalAccessDelegate.java | 11 +++-------- .../timestamp/TimestampsRegionImplTestCase.java | 4 ++-- .../cache/infinispan/util}/ClassLoaderAwareCache.java | 2 +- 4 files changed, 6 insertions(+), 16 deletions(-) rename hibernate-infinispan/src/{main/java/org/hibernate/cache/infinispan/impl => test/java/org/hibernate/test/cache/infinispan/util}/ClassLoaderAwareCache.java (99%) diff --git a/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java b/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java index dd929723c8..42ad99fcbd 100644 --- a/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java +++ b/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/InfinispanRegionFactory.java @@ -39,7 +39,6 @@ import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.CacheException; import org.hibernate.cache.infinispan.collection.CollectionRegionImpl; import org.hibernate.cache.infinispan.entity.EntityRegionImpl; -import org.hibernate.cache.infinispan.impl.ClassLoaderAwareCache; import org.hibernate.cache.infinispan.query.QueryResultsRegionImpl; import org.hibernate.cache.infinispan.timestamp.TimestampTypeOverrides; import org.hibernate.cache.infinispan.timestamp.TimestampsRegionImpl; @@ -563,10 +562,6 @@ public class InfinispanRegionFactory implements RegionFactory { } protected AdvancedCache createCacheWrapper(AdvancedCache cache) { - if (Caches.isClustered(cache)) - return new ClassLoaderAwareCache(cache, - Thread.currentThread().getContextClassLoader()); - return cache; } diff --git a/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java b/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java index 36fcfcd46d..b7d22855a9 100755 --- a/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java +++ b/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/access/TransactionalAccessDelegate.java @@ -27,7 +27,6 @@ import javax.transaction.Transaction; import org.hibernate.cache.infinispan.util.Caches; import org.infinispan.AdvancedCache; -import org.infinispan.context.Flag; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; @@ -53,17 +52,13 @@ public class TransactionalAccessDelegate { private final AdvancedCache cache; private final BaseRegion region; private final PutFromLoadValidator putValidator; - private final AdvancedCache writeCache; - private final AdvancedCache putFromLoadCache; + private final AdvancedCache writeCache; public TransactionalAccessDelegate(BaseRegion region, PutFromLoadValidator validator) { this.region = region; this.cache = region.getCache(); this.putValidator = validator; - this.writeCache = Caches.isInvalidationCache(cache) ? - Caches.ignoreReturnValuesCache(cache, Flag.CACHE_MODE_LOCAL) : - Caches.ignoreReturnValuesCache(cache); - this.putFromLoadCache = Caches.ignoreReturnValuesCache(cache); + this.writeCache = Caches.ignoreReturnValuesCache(cache); } public Object get(Object key, long txTimestamp) throws CacheException { @@ -100,7 +95,7 @@ public class TransactionalAccessDelegate { } try { - putFromLoadCache.putForExternalRead(key, value); + writeCache.putForExternalRead(key, value); } finally { putValidator.releasePutFromLoadLock(key); } diff --git a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java index 0409898acf..f8a661a56e 100644 --- a/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/timestamp/TimestampsRegionImplTestCase.java @@ -42,7 +42,7 @@ import org.infinispan.notifications.cachelistener.event.Event; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cache.infinispan.InfinispanRegionFactory; -import org.hibernate.cache.infinispan.impl.ClassLoaderAwareCache; +import org.hibernate.test.cache.infinispan.util.ClassLoaderAwareCache; import org.hibernate.cache.infinispan.timestamp.TimestampsRegionImpl; import org.hibernate.cache.spi.CacheDataDescription; import org.hibernate.cache.spi.Region; @@ -167,7 +167,7 @@ public class TimestampsRegionImplTestCase extends AbstractGeneralDataRegionTestC SelectedClassnameClassLoader visible = new SelectedClassnameClassLoader(null, null, notFoundClasses, cl); Thread.currentThread().setContextClassLoader(visible); super.event(event); - Thread.currentThread().setContextClassLoader(cl); + Thread.currentThread().setContextClassLoader(cl); } } } diff --git a/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/ClassLoaderAwareCache.java b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/ClassLoaderAwareCache.java similarity index 99% rename from hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/ClassLoaderAwareCache.java rename to hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/ClassLoaderAwareCache.java index d80cd2d691..58010cbcc3 100644 --- a/hibernate-infinispan/src/main/java/org/hibernate/cache/infinispan/impl/ClassLoaderAwareCache.java +++ b/hibernate-infinispan/src/test/java/org/hibernate/test/cache/infinispan/util/ClassLoaderAwareCache.java @@ -1,4 +1,4 @@ -package org.hibernate.cache.infinispan.impl; +package org.hibernate.test.cache.infinispan.util; import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; From 2ff69d24c423d3e95e65606f1c836c975dc2a7c9 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 18 Dec 2012 13:46:40 -0600 Subject: [PATCH 55/58] HHH-7872 - Improved L2 cache storage of "reference" data --- .../action/internal/EntityInsertAction.java | 12 +- .../action/internal/EntityUpdateAction.java | 9 +- .../cache/spi/SharedCacheDelegate.java | 43 ++++ .../hibernate/cache/spi/entry/CacheEntry.java | 138 ++----------- .../spi/entry/ReferenceCacheEntryImpl.java | 75 +++++++ .../spi/entry/StandardCacheEntryImpl.java | 170 ++++++++++++++++ .../cache/spi/entry/StructuredCacheEntry.java | 6 +- .../entry/StructuredCollectionCacheEntry.java | 2 + .../spi/entry/StructuredMapCacheEntry.java | 2 + .../spi/entry/UnstructuredCacheEntry.java | 4 + .../org/hibernate/cfg/AvailableSettings.java | 6 + .../main/java/org/hibernate/cfg/Settings.java | 9 + .../org/hibernate/cfg/SettingsFactory.java | 9 + .../engine/internal/TwoPhaseLoad.java | 9 +- .../internal/DefaultLoadEventListener.java | 188 +++++++++++++++--- .../internal/SessionFactoryImpl.java | 6 +- .../entity/AbstractEntityPersister.java | 159 ++++++++++++++- .../persister/entity/EntityPersister.java | 5 +- .../test/cache/ReferenceCacheTest.java | 140 +++++++++++++ .../GoofyPersisterClassProvider.java | 7 + .../test/legacy/CustomPersister.java | 15 ++ 21 files changed, 825 insertions(+), 189 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java index 198a153c9b..837a947087 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java @@ -108,16 +108,12 @@ public final class EntityInsertAction extends AbstractEntityInsertAction { final SessionFactoryImplementor factory = getSession().getFactory(); if ( isCachePutEnabled( persister, session ) ) { - - CacheEntry ce = new CacheEntry( + CacheEntry ce = persister.buildCacheEntry( + instance, getState(), - persister, - persister.hasUninitializedLazyProperties( instance ), version, - session, - instance - ); - + session + ); cacheEntry = persister.getCacheEntryStructure().structure(ce); final CacheKey ck = session.generateCacheKey( id, persister.getIdentifierType(), persister.getRootEntityName() ); boolean put = persister.getCacheAccessStrategy().insert( ck, cacheEntry, version ); diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java index f37faf5a65..fd875fbb27 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java @@ -185,14 +185,7 @@ public final class EntityUpdateAction extends EntityAction { } else { //TODO: inefficient if that cache is just going to ignore the updated state! - CacheEntry ce = new CacheEntry( - state, - persister, - persister.hasUninitializedLazyProperties( instance ), - nextVersion, - getSession(), - instance - ); + CacheEntry ce = persister.buildCacheEntry( instance,state, nextVersion, getSession() ); cacheEntry = persister.getCacheEntryStructure().structure( ce ); boolean put = persister.getCacheAccessStrategy().update( ck, cacheEntry, nextVersion, previousVersion ); if ( put && factory.getStatistics().isStatisticsEnabled() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java new file mode 100644 index 0000000000..4ef4792a3f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.spi; + +import java.io.Serializable; + +import org.hibernate.persister.entity.EntityPersister; + +/** + * Provides central access to putting and getting data into and out of the shared cache. + *

+ * Scope-wise, this delegate is per-session + * + * @author Steve Ebersole + */ +public interface SharedCacheDelegate { + + public void storeEntity(Object entity, EntityPersister persister, Serializable id); + public Object retrieveEntity(EntityPersister persister, Serializable id, Object optionalEntityInstance); + + +} diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java index 69e4dc6c19..63d847563a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java @@ -25,137 +25,41 @@ package org.hibernate.cache.spi.entry; import java.io.Serializable; -import org.hibernate.AssertionFailure; -import org.hibernate.HibernateException; import org.hibernate.Interceptor; -import org.hibernate.engine.spi.SessionImplementor; -import org.hibernate.event.service.spi.EventListenerGroup; -import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventSource; -import org.hibernate.event.spi.EventType; -import org.hibernate.event.spi.PreLoadEvent; -import org.hibernate.event.spi.PreLoadEventListener; -import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.type.TypeHelper; /** * A cached instance of a persistent class * * @author Gavin King + * @author Steve Ebersole */ -public final class CacheEntry implements Serializable { +public interface CacheEntry extends Serializable { + public boolean isReferenceEntry(); - 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 = TypeHelper.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; - } + /** + * Hibernate stores all entries pertaining to a given entity hierarchy in a single region. This attribute + * tells us the specific entity type represented by the cached data. + * + * @return The entry's exact entity type. + */ + public String getSubclass(); - CacheEntry(Serializable[] state, String subclass, boolean unfetched, Object version) { - this.disassembledState = state; - this.subclass = subclass; - this.lazyPropertiesAreUnfetched = unfetched; - this.version = version; - } + /** + * Retrieves the version (optimistic locking) associated with this cache entry. + * + * @return The version of the entity represented by this entry + */ + public Object getVersion(); - public Object[] assemble( - final Object instance, - final Serializable id, - final EntityPersister persister, - final Interceptor interceptor, - final EventSource session) - throws HibernateException { + public boolean areLazyPropertiesUnfetched(); - 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 = TypeHelper.assemble( - values, - persister.getPropertyTypes(), - session, result - ); - - //persister.setIdentifier(result, id); //before calling interceptor, for consistency with normal load - - //TODO: reuse the PreLoadEvent - final PreLoadEvent preLoadEvent = new PreLoadEvent( session ) - .setEntity( result ) - .setState( assembledProps ) - .setId( id ) - .setPersister( persister ); - - final EventListenerGroup listenerGroup = session - .getFactory() - .getServiceRegistry() - .getService( EventListenerRegistry.class ) - .getEventListenerGroup( EventType.PRE_LOAD ); - for ( PreLoadEventListener listener : listenerGroup.listeners() ) { - listener.onPreLoad( preLoadEvent ); - } - - persister.setPropertyValues( result, assembledProps ); - - 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); - } + // 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. + public Serializable[] getDisassembledState(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java new file mode 100644 index 0000000000..95d00ed8db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java @@ -0,0 +1,75 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.spi.entry; + +import java.io.Serializable; + +import org.hibernate.Interceptor; +import org.hibernate.event.spi.EventSource; +import org.hibernate.persister.entity.EntityPersister; + +/** + * @author Steve Ebersole + */ +public class ReferenceCacheEntryImpl implements CacheEntry { + private final Object reference; + private final String subclass; + + public ReferenceCacheEntryImpl(Object reference, String subclass) { + this.reference = reference; + this.subclass = subclass; + } + + @Override + public boolean isReferenceEntry() { + return true; + } + + @Override + public String getSubclass() { + return subclass; + } + + @Override + public Object getVersion() { + // reference data cannot be versioned + return null; + } + + @Override + public boolean areLazyPropertiesUnfetched() { + // reference data cannot define lazy attributes + return false; + } + + @Override + public Serializable[] getDisassembledState() { + // reference data is not disassembled into the cache + return null; + } + + public Object getReference() { + return reference; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java new file mode 100644 index 0000000000..51f9fceacd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java @@ -0,0 +1,170 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cache.spi.entry; + +import java.io.Serializable; + +import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; +import org.hibernate.Interceptor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.event.service.spi.EventListenerGroup; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.EventSource; +import org.hibernate.event.spi.EventType; +import org.hibernate.event.spi.PreLoadEvent; +import org.hibernate.event.spi.PreLoadEventListener; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.TypeHelper; + +/** + * @author Steve Ebersole + */ +public class StandardCacheEntryImpl implements CacheEntry { + private final Serializable[] disassembledState; + private final String subclass; + private final boolean lazyPropertiesAreUnfetched; + private final Object version; + + @Override + public boolean isReferenceEntry() { + return false; + } + + @Override + 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; + } + + @Override + public String getSubclass() { + return subclass; + } + + @Override + public boolean areLazyPropertiesUnfetched() { + return lazyPropertiesAreUnfetched; + } + + @Override + public Object getVersion() { + return version; + } + + public StandardCacheEntryImpl( + 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 = TypeHelper.disassemble( + state, + persister.getPropertyTypes(), + persister.isLazyPropertiesCacheable() ? + null : persister.getPropertyLaziness(), + session, + owner + ); + subclass = persister.getEntityName(); + lazyPropertiesAreUnfetched = unfetched || !persister.isLazyPropertiesCacheable(); + this.version = version; + } + + StandardCacheEntryImpl(Serializable[] state, String subclass, boolean unfetched, Object version) { + this.disassembledState = state; + this.subclass = subclass; + this.lazyPropertiesAreUnfetched = unfetched; + this.version = version; + } + + public boolean isDeepCopyNeeded() { + // for now always return true. + // todo : See discussion on HHH-7872 + return true; + } + + 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 = TypeHelper.assemble( + values, + persister.getPropertyTypes(), + session, result + ); + + //persister.setIdentifier(result, id); //before calling interceptor, for consistency with normal load + + //TODO: reuse the PreLoadEvent + final PreLoadEvent preLoadEvent = new PreLoadEvent( session ) + .setEntity( result ) + .setState( assembledProps ) + .setId( id ) + .setPersister( persister ); + + final EventListenerGroup listenerGroup = session + .getFactory() + .getServiceRegistry() + .getService( EventListenerRegistry.class ) + .getEventListenerGroup( EventType.PRE_LOAD ); + for ( PreLoadEventListener listener : listenerGroup.listeners() ) { + listener.onPreLoad( preLoadEvent ); + } + + persister.setPropertyValues( result, assembledProps ); + + return assembledProps; + } + + public String toString() { + return "CacheEntry(" + subclass + ')' + ArrayHelper.toString( disassembledState ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java index 095f91606c..90e1f86952 100755 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java @@ -31,7 +31,11 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.persister.entity.EntityPersister; /** + * Structured CacheEntry format for entities. Used to store the entry into the second-level cache + * as a Map so that users can more easily see the cached state. + * * @author Gavin King + * @author Steve Ebersole */ public class StructuredCacheEntry implements CacheEntryStructure { @@ -52,7 +56,7 @@ public class StructuredCacheEntry implements CacheEntryStructure { for ( int i=0; i classMeta = new HashMap(); @@ -380,7 +382,7 @@ public final class SessionFactoryImpl } } - EntityPersister cp = serviceRegistry.getService( PersisterFactory.class ).createEntityPersister( + EntityPersister cp = persisterFactory.createEntityPersister( model, accessStrategy, naturalIdAccessStrategy, @@ -409,7 +411,7 @@ public final class SessionFactoryImpl entityAccessStrategies.put( cacheRegionName, accessStrategy ); cacheAccess.addCacheRegion( cacheRegionName, collectionRegion ); } - CollectionPersister persister = serviceRegistry.getService( PersisterFactory.class ).createCollectionPersister( + CollectionPersister persister = persisterFactory.createCollectionPersister( cfg, model, accessStrategy, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 8d31ca988a..e4177273de 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -55,6 +55,8 @@ import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; +import org.hibernate.cache.spi.entry.ReferenceCacheEntryImpl; +import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.cache.spi.entry.StructuredCacheEntry; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; import org.hibernate.dialect.lock.LockingStrategy; @@ -149,7 +151,7 @@ public abstract class AbstractEntityPersister private final EntityRegionAccessStrategy cacheAccessStrategy; private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy; private final boolean isLazyPropertiesCacheable; - private final CacheEntryStructure cacheEntryStructure; + private final CacheEntryHelper cacheEntryHelper; private final EntityMetamodel entityMetamodel; private final EntityTuplizer entityTuplizer; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -501,9 +503,6 @@ public abstract class AbstractEntityPersister this.cacheAccessStrategy = cacheAccessStrategy; this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy; isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable(); - this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ? - (CacheEntryStructure) new StructuredCacheEntry(this) : - (CacheEntryStructure) new UnstructuredCacheEntry(); this.entityMetamodel = new EntityMetamodel( persistentClass, factory ); this.entityTuplizer = this.entityMetamodel.getTuplizer(); @@ -778,6 +777,48 @@ public abstract class AbstractEntityPersister temporaryIdTableName = persistentClass.getTemporaryIdTableName(); temporaryIdTableDDL = persistentClass.getTemporaryIdTableDDL(); + + this.cacheEntryHelper = buildCacheEntryHelper(); + } + + protected CacheEntryHelper buildCacheEntryHelper() { + if ( cacheAccessStrategy == null ) { + // the entity defined no caching... + return NoopCacheEntryHelper.INSTANCE; + } + + if ( canUseReferenceCacheEntries() ) { + entityMetamodel.setLazy( false ); + // todo : do we also need to unset proxy factory? + return new ReferenceCacheEntryHelper( this ); + } + + return factory.getSettings().isStructuredCacheEntriesEnabled() + ? new StructuredCacheEntryHelper( this ) + : new StandardCacheEntryHelper( this ); + } + + protected boolean canUseReferenceCacheEntries() { + // todo : should really validate that the cache access type is read-only + + if ( ! factory.getSettings().isDirectReferenceCacheEntriesEnabled() ) { + return false; + } + + // for now, limit this to just entities that: + // 1) are immutable + if ( entityMetamodel.isMutable() ) { + return false; + } + + // 2) have no associations. Eventually we want to be a little more lenient with associations. + for ( Type type : getSubclassPropertyTypeClosure() ) { + if ( type.isAssociationType() ) { + return false; + } + } + + return true; } @@ -793,10 +834,6 @@ public abstract class AbstractEntityPersister entityBinding.getHierarchyDetails().getCaching() == null ? false : entityBinding.getHierarchyDetails().getCaching().isCacheLazyProperties(); - this.cacheEntryStructure = - factory.getSettings().isStructuredCacheEntriesEnabled() ? - new StructuredCacheEntry(this) : - new UnstructuredCacheEntry(); this.entityMetamodel = new EntityMetamodel( entityBinding, factory ); this.entityTuplizer = this.entityMetamodel.getTuplizer(); int batch = entityBinding.getBatchSize(); @@ -1104,6 +1141,8 @@ public abstract class AbstractEntityPersister temporaryIdTableName = null; temporaryIdTableDDL = null; + + this.cacheEntryHelper = buildCacheEntryHelper(); } protected static String getTemplateFromString(String string, SessionFactoryImplementor factory) { @@ -4066,10 +4105,16 @@ public abstract class AbstractEntityPersister return cacheAccessStrategy; } + @Override public CacheEntryStructure getCacheEntryStructure() { - return cacheEntryStructure; + return cacheEntryHelper.getCacheEntryStructure(); } - + + @Override + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) { + return cacheEntryHelper.buildCacheEntry( entity, state, version, session ); + } + public boolean hasNaturalIdCache() { return naturalIdRegionAccessStrategy != null; } @@ -4925,5 +4970,97 @@ public abstract class AbstractEntityPersister public int determineTableNumberForColumn(String columnName) { return 0; } - + + /** + * Consolidated these onto a single helper because the 2 pieces work in tandem. + */ + public static interface CacheEntryHelper { + public CacheEntryStructure getCacheEntryStructure(); + + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session); + } + + private static class StandardCacheEntryHelper implements CacheEntryHelper { + private final EntityPersister persister; + + private StandardCacheEntryHelper(EntityPersister persister) { + this.persister = persister; + } + + @Override + public CacheEntryStructure getCacheEntryStructure() { + return UnstructuredCacheEntry.INSTANCE; + } + + @Override + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) { + return new StandardCacheEntryImpl( + state, + persister, + persister.hasUninitializedLazyProperties( entity ), + version, + session, + entity + ); + } + } + + private static class ReferenceCacheEntryHelper implements CacheEntryHelper { + private final EntityPersister persister; + + private ReferenceCacheEntryHelper(EntityPersister persister) { + this.persister = persister; + } + + @Override + public CacheEntryStructure getCacheEntryStructure() { + return UnstructuredCacheEntry.INSTANCE; + } + + @Override + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) { + return new ReferenceCacheEntryImpl( entity, persister.getEntityName() ); + } + } + + private static class StructuredCacheEntryHelper implements CacheEntryHelper { + private final EntityPersister persister; + private final StructuredCacheEntry structure; + + private StructuredCacheEntryHelper(EntityPersister persister) { + this.persister = persister; + this.structure = new StructuredCacheEntry( persister ); + } + + @Override + public CacheEntryStructure getCacheEntryStructure() { + return structure; + } + + @Override + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) { + return new StandardCacheEntryImpl( + state, + persister, + persister.hasUninitializedLazyProperties( entity ), + version, + session, + entity + ); + } + } + + private static class NoopCacheEntryHelper implements CacheEntryHelper { + public static final NoopCacheEntryHelper INSTANCE = new NoopCacheEntryHelper(); + + @Override + public CacheEntryStructure getCacheEntryStructure() { + return UnstructuredCacheEntry.INSTANCE; + } + + @Override + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) { + throw new HibernateException( "Illegal attempt to build cache entry for non-cached entity" ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 29b4faa7ac..f95ea71970 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -35,6 +35,7 @@ import org.hibernate.bytecode.spi.EntityInstrumentationMetadata; import org.hibernate.cache.spi.OptimisticCacheSource; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -475,7 +476,9 @@ public interface EntityPersister extends OptimisticCacheSource { * Get the cache structure */ public CacheEntryStructure getCacheEntryStructure(); - + + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session); + /** * Does this class have a natural id cache */ diff --git a/hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java b/hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java new file mode 100644 index 0000000000..0d65243ea2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java @@ -0,0 +1,140 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.cache; + +import javax.persistence.Cacheable; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.Session; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Immutable; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.persister.entity.EntityPersister; + +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Steve Ebersole + */ +public class ReferenceCacheTest extends BaseCoreFunctionalTestCase { + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.setProperty( AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES, "true" ); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { MyReferenceData.class }; + } + + @Test + public void testUseOfDirectReferencesInCache() throws Exception { + EntityPersister persister = (EntityPersister) sessionFactory().getClassMetadata( MyReferenceData.class ); + assertFalse( persister.isMutable() ); + assertTrue( persister.buildCacheEntry( null, null, null, null ).isReferenceEntry() ); + assertFalse( persister.hasProxy() ); + + final MyReferenceData myReferenceData = new MyReferenceData( 1, "first item", "abc" ); + + // save a reference in one session + Session s = openSession(); + s.beginTransaction(); + s.save( myReferenceData ); + s.getTransaction().commit(); + s.close(); + + // now load it in another + s = openSession(); + s.beginTransaction(); +// MyReferenceData loaded = (MyReferenceData) s.get( MyReferenceData.class, 1 ); + MyReferenceData loaded = (MyReferenceData) s.load( MyReferenceData.class, 1 ); + s.getTransaction().commit(); + s.close(); + + // the 2 instances should be the same (==) + assertTrue( "The two instances were different references", myReferenceData == loaded ); + + // cleanup + s = openSession(); + s.beginTransaction(); + s.delete( myReferenceData ); + s.getTransaction().commit(); + s.close(); + } + + @Entity( name="MyReferenceData" ) + @Immutable + @Cacheable + @Cache( usage = CacheConcurrencyStrategy.READ_ONLY ) +// @Proxy( lazy = false ) + @SuppressWarnings("UnusedDeclaration") + public static class MyReferenceData { + @Id + private Integer id; + private String name; + private String theValue; + + public MyReferenceData(Integer id, String name, String theValue) { + this.id = id; + this.name = name; + this.theValue = theValue; + } + + protected MyReferenceData() { + } + + 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 String getTheValue() { + return theValue; + } + + public void setTheValue(String theValue) { + this.theValue = theValue; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index ee64cfb2d7..3e32129981 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -35,6 +35,7 @@ import org.hibernate.bytecode.spi.EntityInstrumentationMetadata; import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CascadeStyle; @@ -400,6 +401,12 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return null; } + @Override + public CacheEntry buildCacheEntry( + Object entity, Object[] state, Object version, SessionImplementor session) { + return null; + } + @Override public ClassMetadata getClassMetadata() { return null; diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 3289c7a75d..62258c377b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -13,7 +13,9 @@ import org.hibernate.MappingException; import org.hibernate.bytecode.spi.EntityInstrumentationMetadata; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; +import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; import org.hibernate.engine.internal.TwoPhaseLoad; import org.hibernate.engine.spi.CascadeStyle; @@ -566,6 +568,19 @@ public class CustomPersister implements EntityPersister { return new UnstructuredCacheEntry(); } + @Override + public CacheEntry buildCacheEntry( + Object entity, Object[] state, Object version, SessionImplementor session) { + return new StandardCacheEntryImpl( + state, + this, + this.hasUninitializedLazyProperties( entity ), + version, + session, + entity + ); + } + @Override public boolean hasSubselectLoadableCollections() { return false; From 9c3998dade2af4b73d7745fa6e6d2788bd7872ce Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 19 Dec 2012 09:24:36 -0600 Subject: [PATCH 56/58] HHH-7872 - Improved L2 cache storage of "reference" data --- .../cache/spi/SharedCacheDelegate.java | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java deleted file mode 100644 index 4ef4792a3f..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2012, Red Hat Inc. or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Inc. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - */ -package org.hibernate.cache.spi; - -import java.io.Serializable; - -import org.hibernate.persister.entity.EntityPersister; - -/** - * Provides central access to putting and getting data into and out of the shared cache. - *

- * Scope-wise, this delegate is per-session - * - * @author Steve Ebersole - */ -public interface SharedCacheDelegate { - - public void storeEntity(Object entity, EntityPersister persister, Serializable id); - public Object retrieveEntity(EntityPersister persister, Serializable id, Object optionalEntityInstance); - - -} From 12c7ab93c377de74c3ef9f8baba05f19a9c63a51 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 19 Dec 2012 14:48:11 -0500 Subject: [PATCH 57/58] HHH-7797 Simplified UniqueKey creation and corrected a couple of @UniqueConstraint bugs. --- .../java/org/hibernate/cfg/Configuration.java | 25 +++++------------- .../dialect/unique/DefaultUniqueDelegate.java | 15 ----------- .../dialect/unique/UniqueDelegate.java | 19 -------------- .../java/org/hibernate/mapping/Table.java | 9 ++++--- .../hibernate/metamodel/relational/Table.java | 4 ++- .../test/annotations/EntityTest.java | 26 +++++-------------- 6 files changed, 23 insertions(+), 75 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index 48dfaf6e1c..f8391e1455 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -51,6 +51,7 @@ import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarFile; import java.util.zip.ZipEntry; + import javax.persistence.AttributeConverter; import javax.persistence.Embeddable; import javax.persistence.Entity; @@ -60,10 +61,6 @@ import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; -import org.jboss.logging.Logger; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; import org.hibernate.DuplicateMappingException; @@ -82,6 +79,7 @@ import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.dialect.Dialect; @@ -137,7 +135,6 @@ import org.hibernate.mapping.UniqueKey; import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.secure.internal.JACCConfiguration; import org.hibernate.service.ServiceRegistry; -import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.IndexMetadata; import org.hibernate.tool.hbm2ddl.TableMetadata; @@ -148,6 +145,9 @@ import org.hibernate.type.Type; import org.hibernate.type.TypeResolver; import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.UserType; +import org.jboss.logging.Logger; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; /** * An instance of Configuration allows the application @@ -1228,15 +1228,6 @@ public class Configuration implements Serializable { ) ); } - -//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) ); -// } -// } } } @@ -1560,12 +1551,10 @@ public class Configuration implements Serializable { unboundNoLogical.add( new Column( logicalColumnName ) ); } } + UniqueKey uk = table.getOrCreateUniqueKey( keyName ); for ( Column column : columns ) { if ( table.containsColumn( column ) ) { - Column tableColumn = table.getColumn( column ); - tableColumn.setUnique( true ); - Dialect.getDialect().getUniqueDelegate().generateUniqueKey( - table, tableColumn ); + uk.addColumn( column ); unbound.remove( column ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java index 41678aa5b3..09e1d7e75d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/DefaultUniqueDelegate.java @@ -40,21 +40,6 @@ public class DefaultUniqueDelegate implements UniqueDelegate { public DefaultUniqueDelegate( Dialect dialect ) { this.dialect = dialect; } - - @Override - public void generateUniqueKey( org.hibernate.mapping.Table table, - org.hibernate.mapping.Column column ) { - org.hibernate.mapping.UniqueKey uk = table.getOrCreateUniqueKey( - column.getQuotedName( dialect ) + '_' ); - uk.addColumn( column ); - } - - @Override - public void generateUniqueKey( Table table, Column column ) { - UniqueKey uk = table.getOrCreateUniqueKey( column.getColumnName() - .encloseInQuotesIfQuoted( dialect ) + '_' ); - uk.addColumn( column ); - } @Override public String applyUniqueToColumn( org.hibernate.mapping.Column column ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java index ef3728361d..4cd3c346d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/unique/UniqueDelegate.java @@ -44,25 +44,6 @@ import org.hibernate.metamodel.relational.UniqueKey; */ public interface UniqueDelegate { - /** - * If the dialect supports unique constraints, create and add a UniqueKey - * to the Table. - * - * @param table - * @param column - */ - public void generateUniqueKey( org.hibernate.mapping.Table table, - org.hibernate.mapping.Column column ); - - /** - * If the dialect supports unique constraints, create and add a UniqueKey - * to the Table. - * - * @param table - * @param column - */ - public void generateUniqueKey( Table table, Column column ); - /** * If the dialect does not supports unique constraints, this method should * return the syntax necessary to mutate the column definition diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 8aaf8e0064..ebd6329aa8 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -422,8 +422,9 @@ public class Table implements RelationalModel, Serializable { } if ( column.isUnique() ) { - dialect.getUniqueDelegate().generateUniqueKey( - this, column ); + UniqueKey uk = getOrCreateUniqueKey( + column.getQuotedName( dialect ) + '_' ); + uk.addColumn( column ); alter.append( dialect.getUniqueDelegate() .applyUniqueToColumn( column ) ); } @@ -524,7 +525,9 @@ public class Table implements RelationalModel, Serializable { } if ( col.isUnique() ) { - dialect.getUniqueDelegate().generateUniqueKey( this, col ); + UniqueKey uk = getOrCreateUniqueKey( + col.getQuotedName( dialect ) + '_' ); + uk.addColumn( col ); buf.append( dialect.getUniqueDelegate() .applyUniqueToColumn( col ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 69cdb3cf0e..d106685c3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -200,7 +200,9 @@ public class Table extends AbstractTableSpecification implements Exportable { } if ( col.isUnique() ) { - dialect.getUniqueDelegate().generateUniqueKey( this, col ); + UniqueKey uk = getOrCreateUniqueKey( col.getColumnName() + .encloseInQuotesIfQuoted( dialect ) + '_' ); + uk.addColumn( col ); buf.append( dialect.getUniqueDelegate() .applyUniqueToColumn( col ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java index 877461835c..9fbdfa7187 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/EntityTest.java @@ -190,7 +190,6 @@ public class EntityTest extends BaseCoreFunctionalTestCase { int id = 5; Session s; Transaction tx; - s = openSession(); tx = s.beginTransaction(); Sky sky = new Sky(); @@ -198,9 +197,6 @@ public class EntityTest extends BaseCoreFunctionalTestCase { sky.color = "green"; sky.day = "monday"; sky.month = "March"; - s.persist( sky ); - tx.commit(); - s.close(); Sky otherSky = new Sky(); otherSky.id = new Long( id++ ); @@ -214,25 +210,17 @@ public class EntityTest extends BaseCoreFunctionalTestCase { sameSky.day = "monday"; sameSky.month = "March"; - s = openSession(); - tx = s.beginTransaction(); - try { - s.persist( otherSky ); - tx.commit(); - fail( "unique constraints not respected" ); - } - catch (HibernateException e) { - //success - } - finally { - if ( tx != null ) tx.rollback(); - s.close(); - } + s.save( sky ); + s.flush(); + + s.save( otherSky ); + tx.commit(); + s.close(); s = openSession(); tx = s.beginTransaction(); try { - s.persist( sameSky ); + s.save( sameSky ); tx.commit(); fail( "unique constraints not respected" ); } From 0a17d4612a350785d6717c6bfd3f962a2b5d6752 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Wed, 19 Dec 2012 14:48:59 -0500 Subject: [PATCH 58/58] HHH-7872 corrected compiling issue --- .../test/ejb3configuration/PersisterClassProviderTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java index 529618bdd0..e217265aa0 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java @@ -38,6 +38,7 @@ import org.hibernate.MappingException; import org.hibernate.bytecode.spi.EntityInstrumentationMetadata; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntryStructure; import org.hibernate.jpa.test.SettingsGenerator; import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter; @@ -602,6 +603,12 @@ public class PersisterClassProviderTest { public FilterAliasGenerator getFilterAliasGenerator(String rootAlias) { return null; } + + @Override + public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) { + // TODO Auto-generated method stub + return null; + } } public static class GoofyException extends RuntimeException {