-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
SQLStateConversionDelegate.java
138 lines (123 loc) · 5.17 KB
/
SQLStateConversionDelegate.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.exception.internal;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.JDBCException;
import org.hibernate.PessimisticLockException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.hibernate.exception.spi.AbstractSQLExceptionConversionDelegate;
import org.hibernate.exception.spi.ConversionContext;
import org.hibernate.internal.util.JdbcExceptionHelper;
/**
* A SQLExceptionConverter implementation which performs conversion based on the underlying SQLState.
* Interpretation of a SQL error based on SQLState is not nearly as accurate as using the ErrorCode (which is,
* however, vendor-specific).
*
* SQLState codes are defined by both ANSI SQL specs and X/Open. Some of the "classes" are shared, others are
* specific to one or another, yet others are custom vendor classes. Unfortunately I have not been able to
* find a "blessed" list of X/Open codes. These codes are cobbled together between ANSI SQL spec and error
* code tables from few vendors documentation.
*
* @author Steve Ebersole
*/
public class SQLStateConversionDelegate extends AbstractSQLExceptionConversionDelegate {
private static final Set<String> SQL_GRAMMAR_CATEGORIES = buildGrammarCategories();
private static Set<String> buildGrammarCategories() {
HashSet<String> categories = new HashSet<>(
Arrays.asList(
"07", // "dynamic SQL error"
"20",
"2A", // "direct SQL syntax error or access rule violation"
"37", // "dynamic SQL syntax error or access rule violation"
"42", // "syntax error or access rule violation"
"65", // Oracle specific as far as I can tell
"S0" // MySQL specific as far as I can tell
)
);
return Collections.unmodifiableSet( categories );
}
private static final Set DATA_CATEGORIES = buildDataCategories();
private static Set<String> buildDataCategories() {
HashSet<String> categories = new HashSet<>(
Arrays.asList(
"21", // "cardinality violation"
"22" // "data exception"
)
);
return Collections.unmodifiableSet( categories );
}
private static final Set INTEGRITY_VIOLATION_CATEGORIES = buildContraintCategories();
private static Set<String> buildContraintCategories() {
HashSet<String> categories = new HashSet<>(
Arrays.asList(
"23", // "integrity constraint violation"
"27", // "triggered data change violation"
"44" // "with check option violation"
)
);
return Collections.unmodifiableSet( categories );
}
private static final Set CONNECTION_CATEGORIES = buildConnectionCategories();
private static Set<String> buildConnectionCategories() {
HashSet<String> categories = new HashSet<>();
categories.add(
"08" // "connection exception"
);
return Collections.unmodifiableSet( categories );
}
public SQLStateConversionDelegate(ConversionContext conversionContext) {
super( conversionContext );
}
@Override
public JDBCException convert(SQLException sqlException, String message, String sql) {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
if ( sqlState != null ) {
String sqlStateClassCode = JdbcExceptionHelper.determineSqlStateClassCode( sqlState );
if ( sqlStateClassCode != null ) {
if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
return new SQLGrammarException( message, sqlException, sql );
}
else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( sqlStateClassCode ) ) {
final String constraintName = getConversionContext()
.getViolatedConstraintNameExtractor()
.extractConstraintName( sqlException );
return new ConstraintViolationException( message, sqlException, sql, constraintName );
}
else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
return new JDBCConnectionException( message, sqlException, sql );
}
else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
return new DataException( message, sqlException, sql );
}
}
if ( "40001".equals( sqlState ) ) {
return new LockAcquisitionException( message, sqlException, sql );
}
if ( "40XL1".equals( sqlState ) || "40XL2".equals( sqlState )) {
// Derby "A lock could not be obtained within the time requested."
return new PessimisticLockException( message, sqlException, sql );
}
// MySQL Query execution was interrupted
if ( "70100".equals( sqlState ) ||
// Oracle user requested cancel of current operation
( "72000".equals( sqlState ) && errorCode == 1013 ) ) {
throw new QueryTimeoutException( message, sqlException, sql );
}
}
return null;
}
}