HHH-9166 handle nested exceptions with TemplatedViolatedConstraintNameExtracter

This commit is contained in:
Brett Meyer 2015-06-26 15:19:20 -04:00
parent 1e54ee3dc1
commit 11ae0f72c8
11 changed files with 116 additions and 87 deletions

View File

@ -6,11 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.cfg.Environment;
@ -47,6 +42,11 @@ import org.hibernate.sql.CacheJoinFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.type.StandardBasicTypes;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
/**
* Caché 2007.1 dialect.
*
@ -669,7 +669,7 @@ public class Cache71Dialect extends Dialect {
*/
public static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
@Override
public String extractConstraintName(SQLException sqle) {
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() );
}
};

View File

@ -6,9 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.JDBCException;
import org.hibernate.PessimisticLockException;
import org.hibernate.boot.TempTableDdlTransactionHandling;
@ -37,9 +34,11 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.StandardBasicTypes;
import org.jboss.logging.Logger;
import java.sql.SQLException;
import java.sql.Types;
/**
* A dialect compatible with the H2 database.
*
@ -335,7 +334,8 @@ public class H2Dialect extends Dialect {
* @param sqle The exception that was the result of the constraint violation.
* @return The extracted constraint name.
*/
public String extractConstraintName(SQLException sqle) {
@Override
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
String constraintName = null;
// 23000: Check constraint violation: {0}
// 23001: Unique index or primary key violation: {0}

View File

@ -6,11 +6,6 @@
*/
package org.hibernate.dialect;
import java.io.Serializable;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
@ -46,9 +41,13 @@ import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.type.StandardBasicTypes;
import org.jboss.logging.Logger;
import java.io.Serializable;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
/**
* An SQL dialect compatible with HSQLDB (HyperSQL).
* <p/>
@ -393,7 +392,7 @@ public class HSQLDialect extends Dialect {
private static final ViolatedConstraintNameExtracter EXTRACTER_18 = new TemplatedViolatedConstraintNameExtracter() {
@Override
public String extractConstraintName(SQLException sqle) {
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
String constraintName = null;
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
@ -430,7 +429,7 @@ public class HSQLDialect extends Dialect {
*/
private static final ViolatedConstraintNameExtracter EXTRACTER_20 = new TemplatedViolatedConstraintNameExtracter() {
@Override
public String extractConstraintName(SQLException sqle) {
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
String constraintName = null;
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );

View File

@ -6,10 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
import org.hibernate.MappingException;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.pagination.FirstLimitHandler;
@ -26,6 +22,10 @@ import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.type.StandardBasicTypes;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
/**
* Informix dialect.<br>
* <br>
@ -223,7 +223,7 @@ public class InformixDialect extends Dialect {
private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
@Override
public String extractConstraintName(SQLException sqle) {
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
String constraintName = null;
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );

View File

@ -6,13 +6,13 @@
*/
package org.hibernate.dialect;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper;
import java.sql.SQLException;
import java.sql.Types;
/**
* An SQL dialect for MySQL 5.x specific features.
*
@ -38,17 +38,13 @@ public class MySQL5Dialect extends MySQLDialect {
private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
public String extractConstraintName(SQLException sqle) {
try {
final int sqlState = Integer.valueOf( JdbcExceptionHelper.extractSqlState( sqle ) ).intValue();
switch ( sqlState ) {
case 23000:
return extractUsingTemplate( " for key '", "'", sqle.getMessage() );
default:
return null;
}
}
catch ( NumberFormatException nfe ) {
@Override
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
final int sqlState = Integer.valueOf( JdbcExceptionHelper.extractSqlState( sqle ) ).intValue();
switch ( sqlState ) {
case 23000:
return extractUsingTemplate( " for key '", "'", sqle.getMessage() );
default:
return null;
}
}

View File

@ -6,13 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Locale;
import org.hibernate.JDBCException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.annotations.common.util.StringHelper;
@ -47,6 +40,13 @@ import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.sql.BitTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Locale;
/**
* A dialect for Oracle 8i.
*
@ -469,7 +469,8 @@ public class Oracle8iDialect extends Dialect {
* @param sqle The exception that was the result of the constraint violation.
* @return The extracted constraint name.
*/
public String extractConstraintName(SQLException sqle) {
@Override
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
if ( errorCode == 1 || errorCode == 2291 || errorCode == 2292 ) {
return extractUsingTemplate( "(", ")", sqle.getMessage() );

View File

@ -6,12 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.NvlFunction;
@ -21,15 +15,20 @@ import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy;
import org.hibernate.hql.spi.id.local.AfterUseAction;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.type.StandardBasicTypes;
import org.jboss.logging.Logger;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
/**
* An SQL dialect for Oracle 9 (uses ANSI-style syntax where possible).
*
@ -299,7 +298,7 @@ public class Oracle9Dialect extends Dialect {
private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
@Override
public String extractConstraintName(SQLException sqle) {
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
if ( errorCode == 1 || errorCode == 2291 || errorCode == 2292 ) {
return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() );

View File

@ -6,11 +6,6 @@
*/
package org.hibernate.dialect;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.JDBCException;
import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException;
@ -41,6 +36,11 @@ import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
/**
* An SQL dialect for Postgres
* <p/>
@ -406,26 +406,22 @@ public class PostgreSQL81Dialect extends Dialect {
* Orginally contributed by Denny Bartelt.
*/
private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
public String extractConstraintName(SQLException sqle) {
try {
final int sqlState = Integer.valueOf( JdbcExceptionHelper.extractSqlState( sqle ) );
switch (sqlState) {
// CHECK VIOLATION
case 23514: return extractUsingTemplate( "violates check constraint \"","\"", sqle.getMessage() );
// UNIQUE VIOLATION
case 23505: return extractUsingTemplate( "violates unique constraint \"","\"", sqle.getMessage() );
// FOREIGN KEY VIOLATION
case 23503: return extractUsingTemplate( "violates foreign key constraint \"","\"", sqle.getMessage() );
// NOT NULL VIOLATION
case 23502: return extractUsingTemplate( "null value in column \"","\" violates not-null constraint", sqle.getMessage() );
// TODO: RESTRICT VIOLATION
case 23001: return null;
// ALL OTHER
default: return null;
}
}
catch (NumberFormatException nfe) {
return null;
@Override
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
final int sqlState = Integer.valueOf( JdbcExceptionHelper.extractSqlState( sqle ) );
switch (sqlState) {
// CHECK VIOLATION
case 23514: return extractUsingTemplate( "violates check constraint \"","\"", sqle.getMessage() );
// UNIQUE VIOLATION
case 23505: return extractUsingTemplate( "violates unique constraint \"","\"", sqle.getMessage() );
// FOREIGN KEY VIOLATION
case 23503: return extractUsingTemplate( "violates foreign key constraint \"","\"", sqle.getMessage() );
// NOT NULL VIOLATION
case 23502: return extractUsingTemplate( "null value in column \"","\" violates not-null constraint", sqle.getMessage() );
// TODO: RESTRICT VIOLATION
case 23001: return null;
// ALL OTHER
default: return null;
}
}
};

View File

@ -144,7 +144,7 @@ public class Teradata14Dialect extends TeradataDialect {
* @return The extracted constraint name.
*/
@Override
public String extractConstraintName(SQLException sqle) {
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
String constraintName = null;
int errorCode = sqle.getErrorCode();

View File

@ -6,14 +6,41 @@
*/
package org.hibernate.exception.spi;
import java.sql.SQLException;
/**
* Knows how to extract a violated constraint name from an error message based on the
* fact that the constraint name is templated within the message.
*
* @author Steve Ebersole
* @author Brett Meyer
*/
public abstract class TemplatedViolatedConstraintNameExtracter implements ViolatedConstraintNameExtracter {
@Override
public String extractConstraintName(SQLException sqle) {
try {
String constraintName = null;
// handle nested exceptions
do {
constraintName = doExtractConstraintName(sqle);
if (sqle.getNextException() == null
|| sqle.getNextException() == sqle) {
break;
} else {
sqle = sqle.getNextException();
}
} while (constraintName == null);
return constraintName;
} catch (NumberFormatException nfe) {
return null;
}
}
protected abstract String doExtractConstraintName(SQLException sqle) throws NumberFormatException;
/**
* Extracts the constraint name based on a template (i.e., <i>templateStart</i><b>constraintName</b><i>templateEnd</i>).
*

View File

@ -6,11 +6,6 @@
*/
package org.hibernate.dialect;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.sql.SQLException;
import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
@ -21,13 +16,19 @@ import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import java.sql.BatchUpdateException;
import java.sql.SQLException;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Testing of patched support for PostgreSQL Lock error detection. HHH-7251
*
* @author Bryan Varner
*/
@TestForIssue( jiraKey = "HHH-7251" )
public class PostgreSQL81DialectTestCase extends BaseUnitTestCase {
@Test
@ -67,4 +68,14 @@ public class PostgreSQL81DialectTestCase extends BaseUnitTestCase {
forUpdateClause = dialect.getForUpdateString("tableAlias1,tableAlias2", lockOptions);
assertTrue("for update of tableAlias1,tableAlias2".equals(forUpdateClause));
}
@Test
public void testExtractConstraintName() {
PostgreSQL81Dialect dialect = new PostgreSQL81Dialect();
SQLException psqlException = new java.sql.SQLException("ERROR: duplicate key value violates unique constraint \"uk_4bm1x2ultdmq63y3h5r3eg0ej\" Detail: Key (username, server_config)=(user, 1) already exists.", "23505");
BatchUpdateException batchUpdateException = new BatchUpdateException("Concurrent Error", "23505", null);
batchUpdateException.setNextException(psqlException);
String constraintName = dialect.getViolatedConstraintNameExtracter().extractConstraintName(batchUpdateException);
assertThat(constraintName, is("uk_4bm1x2ultdmq63y3h5r3eg0ej"));
}
}