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 c2ca7315d522..7eb7f611530f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2804,4 +2804,15 @@ public void augmentRecognizedTableTypes(List tableTypesList) { public boolean supportsPartitionBy() { return false; } + + /** + * Override the DatabaseMetaData#supportsNamedParameters() + * + * @return boolean + * + * @throws SQLException Accessing the DatabaseMetaData can throw it. Just re-throw and Hibernate will handle. + */ + public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { + return databaseMetaData != null && databaseMetaData.supportsNamedParameters(); + } } 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 f2cee5772c84..32ca3a724423 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -51,6 +51,7 @@ import org.hibernate.type.StandardBasicTypes; import org.jboss.logging.Logger; +import java.sql.DatabaseMetaData; /** * An SQL dialect compatible with HSQLDB (HyperSQL). @@ -669,4 +670,9 @@ public IdentityColumnSupport getIdentityColumnSupport() { public NameQualifierSupport getNameQualifierSupport() { return NameQualifierSupport.SCHEMA; } + + @Override + public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java index 2d2a85b26668..11f1ed780d82 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java @@ -192,7 +192,6 @@ public Builder apply(DatabaseMetaData databaseMetaData) throws SQLException { lobLocatorUpdateCopy = databaseMetaData.locatorsUpdateCopy(); typeInfoSet = new LinkedHashSet(); typeInfoSet.addAll( TypeInfo.extractTypeInfo( databaseMetaData ) ); - return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java index 51311dc7c5b7..9674322020b9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java @@ -75,7 +75,6 @@ public JdbcEnvironmentImpl(ServiceRegistryImplementor serviceRegistry, Dialect d this.nameQualifierSupport = nameQualifierSupport; this.sqlExceptionHelper = buildSqlExceptionHelper( dialect, logWarnings( cfgService, dialect ) ); - this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl.Builder( this ).build(); final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this ); identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) ); @@ -84,8 +83,10 @@ public JdbcEnvironmentImpl(ServiceRegistryImplementor serviceRegistry, Dialect d identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport ); IdentifierHelper identifierHelper = null; + ExtractedDatabaseMetaDataImpl.Builder dbMetaDataBuilder = new ExtractedDatabaseMetaDataImpl.Builder( this ); try { identifierHelper = dialect.buildIdentifierHelper( identifierHelperBuilder, null ); + dbMetaDataBuilder.setSupportsNamedParameters( dialect.supportsNamedParameters( null ) ); } catch (SQLException sqle) { // should never ever happen @@ -96,6 +97,8 @@ public JdbcEnvironmentImpl(ServiceRegistryImplementor serviceRegistry, Dialect d } this.identifierHelper = identifierHelper; + this.extractedMetaDataSupport = dbMetaDataBuilder.build(); + this.currentCatalog = identifierHelper.toIdentifier( cfgService.getSetting( AvailableSettings.DEFAULT_CATALOG, StandardConverters.STRING ) ); @@ -152,6 +155,7 @@ public JdbcEnvironmentImpl(DatabaseMetaData databaseMetaData, Dialect dialect) t this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl.Builder( this ) .apply( databaseMetaData ) + .setSupportsNamedParameters( databaseMetaData.supportsNamedParameters() ) .build(); NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport(); @@ -226,6 +230,7 @@ public JdbcEnvironmentImpl( this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl.Builder( this ) .apply( databaseMetaData ) .setConnectionSchemaName( determineCurrentSchemaName( databaseMetaData, serviceRegistry, dialect ) ) + .setSupportsNamedParameters(dialect.supportsNamedParameters(databaseMetaData)) .build(); NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport(); diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/procedure/HSQLStoreProcedureTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/procedure/HSQLStoreProcedureTest.java new file mode 100644 index 000000000000..261530e1bd90 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/procedure/HSQLStoreProcedureTest.java @@ -0,0 +1,141 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.jpa.test.procedure; + +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.ParameterMode; +import javax.persistence.StoredProcedureParameter; +import javax.persistence.StoredProcedureQuery; +import javax.persistence.Table; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; + +import static org.junit.Assert.fail; + +/** + * @author Andrea Boriero + */ +@TestForIssue(jiraKey = "HHH-10515") +@RequiresDialect(value = HSQLDialect.class) +public class HSQLStoreProcedureTest extends BaseEntityManagerFunctionalTestCase { + EntityManagerFactory entityManagerFactory; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] {User.class}; + } + + @Before + public void startUp() { + entityManagerFactory = getOrCreateEntityManager().getEntityManagerFactory(); + + createProcedures( entityManagerFactory ); + } + + @After + public void tearDown() { + dropProcedures( entityManagerFactory ); + } + + @Test + public void testNamedStoredProcedureExecution() { + EntityManager em = entityManagerFactory.createEntityManager(); + try { + StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "User.inoutproc" ); + query.setParameter( "arg1", 1 ); + query.execute(); + } + finally { + em.close(); + } + } + + private void createProcedures(EntityManagerFactory emf) { + final String procedureStatement = "CREATE procedure inoutproc (IN arg1 int, OUT res int) " + + "BEGIN ATOMIC set res = arg1 + 1;" + + "END"; + executeStatement( emf, procedureStatement ); + } + + private void dropProcedures(EntityManagerFactory emf) { + executeStatement( emf, "DROP procedure inoutproc" ); + } + + public void executeStatement(EntityManagerFactory emf, String toExecute) { + final SessionFactoryImplementor sf = emf.unwrap( SessionFactoryImplementor.class ); + final JdbcConnectionAccess connectionAccess = sf.getServiceRegistry() + .getService( JdbcServices.class ) + .getBootstrapJdbcConnectionAccess(); + final Connection conn; + try { + conn = connectionAccess.obtainConnection(); + conn.setAutoCommit( false ); + + try { + Statement statement = conn.createStatement(); + statement.execute( toExecute ); + + try { + statement.close(); + } + catch (SQLException e) { + fail( e.getMessage() ); + } + } + finally { + try { + conn.commit(); + } + catch (SQLException e) { + fail( e.getMessage() ); + } + + try { + connectionAccess.releaseConnection( conn ); + } + catch (SQLException e) { + fail( e.getMessage() ); + } + } + } + catch (SQLException e) { + throw new RuntimeException( "Unable to create stored procedures", e ); + } + } + + @Entity(name = "User") + @NamedStoredProcedureQuery(name = "User.inoutproc", procedureName = "inoutproc", parameters = { + @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg1", type = Integer.class), + @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) + }) + @Table(name = "USERS") + public class User { + + @Id + @GeneratedValue + private Integer id; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/env/NoDatabaseMetaDataTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/env/NoDatabaseMetaDataTest.java new file mode 100644 index 000000000000..017bc525705a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/env/NoDatabaseMetaDataTest.java @@ -0,0 +1,91 @@ +/** + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.jdbc.env; + +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +public class NoDatabaseMetaDataTest extends BaseUnitTestCase { + + @Test + @TestForIssue( jiraKey = "HHH-10515" ) + public void testNoJdbcMetadataDefaultDialect() { + final StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() + .applySetting( "hibernate.temp.use_jdbc_metadata_defaults", "false" ) + .build(); + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + ExtractedDatabaseMetaData extractedDatabaseMetaData = jdbcEnvironment.getExtractedDatabaseMetaData(); + + assertNull( extractedDatabaseMetaData.getConnectionCatalogName() ); + assertNull( extractedDatabaseMetaData.getConnectionSchemaName() ); + assertTrue( extractedDatabaseMetaData.getTypeInfoSet().isEmpty() ); + assertTrue( extractedDatabaseMetaData.getExtraKeywords().isEmpty() ); + assertFalse( extractedDatabaseMetaData.supportsNamedParameters() ); + assertFalse( extractedDatabaseMetaData.supportsRefCursors() ); + assertFalse( extractedDatabaseMetaData.supportsScrollableResults() ); + assertFalse( extractedDatabaseMetaData.supportsGetGeneratedKeys() ); + assertFalse( extractedDatabaseMetaData.supportsBatchUpdates() ); + assertFalse( extractedDatabaseMetaData.supportsDataDefinitionInTransaction() ); + assertFalse( extractedDatabaseMetaData.doesDataDefinitionCauseTransactionCommit() ); + assertNull( extractedDatabaseMetaData.getSqlStateType() ); + assertFalse( extractedDatabaseMetaData.doesLobLocatorUpdateCopy() ); + + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + + @Test + @TestForIssue( jiraKey = "HHH-10515" ) + public void testNoJdbcMetadataDialectOverride() { + final StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() + .applySetting( "hibernate.temp.use_jdbc_metadata_defaults", "false" ) + .applySetting( AvailableSettings.DIALECT, TestDialect.class.getName() ) + .build(); + JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); + ExtractedDatabaseMetaData extractedDatabaseMetaData = jdbcEnvironment.getExtractedDatabaseMetaData(); + + assertNull( extractedDatabaseMetaData.getConnectionCatalogName() ); + assertNull( extractedDatabaseMetaData.getConnectionSchemaName() ); + assertTrue( extractedDatabaseMetaData.getTypeInfoSet().isEmpty() ); + assertTrue( extractedDatabaseMetaData.getExtraKeywords().isEmpty() ); + assertTrue( extractedDatabaseMetaData.supportsNamedParameters() ); + assertFalse( extractedDatabaseMetaData.supportsRefCursors() ); + assertFalse( extractedDatabaseMetaData.supportsScrollableResults() ); + assertFalse( extractedDatabaseMetaData.supportsGetGeneratedKeys() ); + assertFalse( extractedDatabaseMetaData.supportsBatchUpdates() ); + assertFalse( extractedDatabaseMetaData.supportsDataDefinitionInTransaction() ); + assertFalse( extractedDatabaseMetaData.doesDataDefinitionCauseTransactionCommit() ); + assertNull( extractedDatabaseMetaData.getSqlStateType() ); + assertFalse( extractedDatabaseMetaData.doesLobLocatorUpdateCopy() ); + + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + + public static class TestDialect extends Dialect { + @Override + public boolean supportsNamedParameters(java.sql.DatabaseMetaData databaseMetaData) { + return true; + } + } + +} +