HHH-7357 - Constraint violation exception while inserting NULL to not nullable column

This commit is contained in:
Lukasz Antoniak 2013-03-30 18:02:48 +01:00 committed by Brett Meyer
parent b838344eeb
commit ed9d7f2fa2
6 changed files with 87 additions and 20 deletions

View File

@ -30,6 +30,7 @@ import org.hibernate.dialect.function.AvgWithArgumentCastFunction;
import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
@ -313,6 +314,12 @@ public class H2Dialect extends Dialect {
exception = new PessimisticLockException(message, sqlException, sql); exception = new PessimisticLockException(message, sqlException, sql);
} }
if ( 90006 == errorCode ) {
// NULL not allowed for column [90006-145]
final String constraintName = getViolatedConstraintNameExtracter().extractConstraintName( sqlException );
exception = new ConstraintViolationException( message, sqlException, sql, constraintName );
}
return exception; return exception;
} }
}; };

View File

@ -37,6 +37,7 @@ import org.hibernate.dialect.function.NvlFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
@ -462,6 +463,14 @@ public class Oracle8iDialect extends Dialect {
} }
// data integrity violation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( 1407 == errorCode ) {
// ORA-01407: cannot update column to NULL
final String constraintName = getViolatedConstraintNameExtracter().extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName );
}
return null; return null;
} }
}; };

View File

@ -31,6 +31,7 @@ import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.QueryTimeoutException; import org.hibernate.QueryTimeoutException;
import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockAcquisitionException; import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
@ -104,9 +105,15 @@ public class SybaseASE157Dialect extends SybaseASE15Dialect {
@Override @Override
public JDBCException convert(SQLException sqlException, String message, String sql) { public JDBCException convert(SQLException sqlException, String message, String sql) {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
if("JZ0TO".equals( sqlState ) || "JZ006".equals( sqlState )){ if("JZ0TO".equals( sqlState ) || "JZ006".equals( sqlState )){
throw new LockTimeoutException( message, sqlException, sql ); throw new LockTimeoutException( message, sqlException, sql );
} }
if ( 515 == errorCode && "ZZZZZ".equals( sqlState ) ) {
// Attempt to insert NULL value into column; column does not allow nulls.
final String constraintName = getViolatedConstraintNameExtracter().extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName );
}
return null; return null;
} }
}; };

View File

@ -112,7 +112,8 @@ public class SQLStateConversionDelegate extends AbstractSQLExceptionConversionDe
@Override @Override
public JDBCException convert(SQLException sqlException, String message, String sql) { public JDBCException convert(SQLException sqlException, String message, String sql) {
String sqlState = JdbcExceptionHelper.extractSqlState( sqlException ); final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
if ( sqlState != null ) { if ( sqlState != null ) {
String sqlStateClassCode = JdbcExceptionHelper.determineSqlStateClassCode( sqlState ); String sqlStateClassCode = JdbcExceptionHelper.determineSqlStateClassCode( sqlState );
@ -146,8 +147,8 @@ public class SQLStateConversionDelegate extends AbstractSQLExceptionConversionDe
// MySQL Query execution was interrupted // MySQL Query execution was interrupted
if ( "70100".equals( sqlState ) || if ( "70100".equals( sqlState ) ||
// Oracle user requested cancel of current operation // Oracle user requested cancel of current operation
"72000".equals( sqlState ) ) { ( "72000".equals( sqlState ) && errorCode == 1013 ) ) {
throw new QueryTimeoutException( message, sqlException, sql ); throw new QueryTimeoutException( message, sqlException, sql );
} }
} }

View File

@ -26,16 +26,21 @@ package org.hibernate.test.exception;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types;
import org.junit.Test; import org.junit.Test;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.dialect.MySQLMyISAMDialect; import org.hibernate.dialect.MySQLMyISAMDialect;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.ResultSetReturn;
import org.hibernate.engine.jdbc.spi.StatementPreparer;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.SQLGrammarException; import org.hibernate.exception.SQLGrammarException;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -78,14 +83,7 @@ public class SQLExceptionConversionTest extends BaseCoreFunctionalTestCase {
// expected outcome // expected outcome
} }
finally { finally {
if ( ps != null ) { releaseStatement( session, ps );
try {
((SessionImplementor)session).getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
catch( Throwable ignore ) {
// ignore...
}
}
} }
} }
} }
@ -116,14 +114,7 @@ public class SQLExceptionConversionTest extends BaseCoreFunctionalTestCase {
// expected outcome // expected outcome
} }
finally { finally {
if ( ps != null ) { releaseStatement( session, ps );
try {
((SessionImplementor)session).getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
catch( Throwable ignore ) {
// ignore...
}
}
} }
} }
} }
@ -132,4 +123,56 @@ public class SQLExceptionConversionTest extends BaseCoreFunctionalTestCase {
session.getTransaction().rollback(); session.getTransaction().rollback();
session.close(); session.close();
} }
@Test
@TestForIssue(jiraKey = "HHH-7357")
public void testNotNullConstraint() {
final Session session = openSession();
session.beginTransaction();
final User user = new User();
user.setUsername( "Lukasz" );
session.save( user );
session.flush();
session.doWork(
new Work() {
@Override
public void execute(Connection connection) throws SQLException {
final JdbcCoordinator jdbcCoordinator = ( (SessionImplementor) session ).getTransactionCoordinator().getJdbcCoordinator();
final StatementPreparer statementPreparer = jdbcCoordinator.getStatementPreparer();
final ResultSetReturn resultSetReturn = jdbcCoordinator.getResultSetReturn();
PreparedStatement ps = null;
try {
ps = statementPreparer.prepareStatement( "UPDATE T_USER SET user_name = ? WHERE user_id = ?" );
ps.setNull( 1, Types.VARCHAR ); // Attempt to update user name to NULL (NOT NULL constraint defined).
ps.setLong( 2, user.getId() );
resultSetReturn.executeUpdate( ps );
fail( "UPDATE should have failed because of not NULL constraint." );
}
catch ( ConstraintViolationException ignore ) {
// expected outcome
}
finally {
releaseStatement( session, ps );
}
}
}
);
session.getTransaction().rollback();
session.close();
}
private void releaseStatement(Session session, PreparedStatement ps) {
if ( ps != null ) {
try {
( (SessionImplementor) session ).getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
catch ( Throwable ignore ) {
// ignore...
}
}
}
} }

View File

@ -6,7 +6,7 @@
<id name="id" unsaved-value="null" column="user_id" > <id name="id" unsaved-value="null" column="user_id" >
<generator class="native"/> <generator class="native"/>
</id> </id>
<property name="username" type="string" column="user_name" /> <property name="username" type="string" column="user_name" not-null="true" />
<set name="memberships" inverse="false" table="T_MEMBERSHIP" cascade="none"> <set name="memberships" inverse="false" table="T_MEMBERSHIP" cascade="none">
<key column="user_id"/> <key column="user_id"/>
<many-to-many class="Group" column="group_id"/> <many-to-many class="Group" column="group_id"/>