/* * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cn.org.rapid_framework.generator.util.sqlerrorcode; import java.sql.Connection; import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.Map; import java.util.WeakHashMap; import javax.sql.DataSource; import cn.org.rapid_framework.generator.util.DBHelper; import cn.org.rapid_framework.generator.util.GLogger; import cn.org.rapid_framework.generator.util.PatternMatchHelper; /** * Factory for creating {@link SQLErrorCodes} based on the * "databaseProductName" taken from the {@link java.sql.DatabaseMetaData}. * * <p>Returns <code>SQLErrorCodes</code> populated with vendor codes * defined in a configuration file named "sql-error-codes.xml". * Reads the default file in this package if not overridden by a file in * the root of the class path (for example in the "/WEB-INF/classes" directory). * * @author Thomas Risberg * @author Rod Johnson * @author Juergen Hoeller * @see java.sql.DatabaseMetaData#getDatabaseProductName() */ public class SQLErrorCodesFactory { /** * Keep track of a single instance so we can return it to classes that request it. */ private static final SQLErrorCodesFactory instance = new SQLErrorCodesFactory(); /** * Return the singleton instance. */ public static SQLErrorCodesFactory getInstance() { return instance; } /** * Map to hold error codes for all databases defined in the config file. * Key is the database product name, value is the SQLErrorCodes instance. */ private final Map<String, SQLErrorCodes> errorCodesMap = new LinkedHashMap<String, SQLErrorCodes>(); /** * Map to cache the SQLErrorCodes instance per DataSource. */ private final Map<DataSource, SQLErrorCodes> dataSourceCache = new WeakHashMap<DataSource, SQLErrorCodes>(16); public SQLErrorCodesFactory() { //ORA-01400: 无法将NULL插入 //ORA-01722: 无效数字 //ORA-02291: 外键约束 //ORA-02292: An attempt was made to delete a row that is referenced by a foreign key. //ORA-12899: value too large for column "APAYFUND"."PDC_CARD"."CARD_NO" (actual: 32, maximum: 16) //ORA-00001 unique constraint "APAYFUND"."PDC_CARD"."CARD_NO" violated //ORA-02290 check constraint "APAYFUND"."PDC_CARD"."CARD_NO" violated //ORA-01461: can bind a LONG value only for insert into a LONG column //ORA-01438: value larger than specified precision allowed for this column //ms-sql //1215 SQLSTATE: HY000 (ER_CANNOT_ADD_FOREIGN) Cannot add foreign key constraint //1216 SQLSTATE: 23000 (ER_NO_REFERENCED_ROW) Cannot add or update a child row: a foreign key constraint fails //1217 SQLSTATE: 23000 (ER_ROW_IS_REFERENCED) Cannot delete or update a parent row: a foreign key constraint fails //MySQL /* * 1452: 不能删除或更新父行,外键约束失败(%s)。 * 1453: 消息:不能添加或更新子行,外键约束失败(%s)。错误: * 1217: 无法添加或更新子行,外键约束失败。 * 1218: 无法删除或更新父行,外键约束失败。 * 1062: Duplicate entry '123' for key * 1048 SQLState:23000 errorCodeTranslatorDataBaaseName:[MySQL] Column 'reason_detail' cannot be null */ //PostgreSQL /* 23000 违反完整性约束(INTEGRITY CONSTRAINT VIOLATION) 23001 违反限制(RESTRICT VIOLATION) 23502 违反非空(NOT NULL VIOLATION) 23503 违反外键约束(FOREIGN KEY VIOLATION) 23505 违反唯一约束(UNIQUE VIOLATION) 23514 违反检查(CHECK VIOLATION) */ // e.getErrorCode() e.getSQLState() errorCodesMap.put("DB2", newSQLErrorCodes(false,"DB2*","-407,-530,-531,-532,-543,-544,-545,-603,-667")); errorCodesMap.put("Derby", newSQLErrorCodes(true,"Apache Derby","22001,22005,23502,23503,23513,X0Y32")); errorCodesMap.put("H2", newSQLErrorCodes(false,"H2","22003,22012,22025,23000,23001,23002,23003")); // http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html errorCodesMap.put("HSQL", newSQLErrorCodes(false,"HSQL Database Engine","-9")); errorCodesMap.put("Informix", newSQLErrorCodes(false,"Informix","-692,-11030")); errorCodesMap.put("MS-SQL", newSQLErrorCodes(false,"Microsoft SQL Server","2627,8114,8115")); errorCodesMap.put("MySQL", newSQLErrorCodes(false,"MySQL","1217,1218,1452,1453,1062,1406,1048,630,839,840,893,1169,1215,1216,1217,1218,1451,1452,1453,1557,1264")); //spring error code定义:630,839,840,893,1169,1215,1216,1217,1218,1451,1452,1453,1557 errorCodesMap.put("Oracle", newSQLErrorCodes(false,"Oracle","2291,2292,1400,1722,12899,1,2290,1461,1438")); // TODO spring定义: 2291,2292,1400,1722 errorCodesMap.put("PostgreSQL", newSQLErrorCodes(true,"PostgreSQL","23001,23503,23514")); //全部约束: "23000,23001,23502,23503,23505,23514" errorCodesMap.put("Sybase", newSQLErrorCodes(false,"Sybase SQL Server,SQL Server,Adaptive Server Enterprise,ASE,sql server","233,423,511,515,530,547,2615,2714")); } public SQLErrorCodes newSQLErrorCodes(boolean useStateCodeForTranslation,String databaseProductNames,String dataIntegrityViolationCodes) { SQLErrorCodes r = new SQLErrorCodes(); r.setDatabaseProductNames(databaseProductNames.split(",")); r.setDataIntegrityViolationCodes(dataIntegrityViolationCodes.split(",")); r.setUseSqlStateForTranslation(useStateCodeForTranslation); return r; } /** * Return the {@link SQLErrorCodes} instance for the given database. * <p>No need for a database metadata lookup. * @param dbName the database name (must not be <code>null</code>) * @return the <code>SQLErrorCodes</code> instance for the given database * @throws IllegalArgumentException if the supplied database name is <code>null</code> */ public SQLErrorCodes getErrorCodes(String dbName) { if(dbName == null) throw new IllegalArgumentException("Database product name must not be null"); SQLErrorCodes sec = this.errorCodesMap.get(dbName); if (sec == null) { for (SQLErrorCodes candidate : this.errorCodesMap.values()) { if (PatternMatchHelper.simpleMatch(candidate.getDatabaseProductNames(), dbName)) { sec = candidate; break; } } } if (sec != null) { GLogger.debug("SQL error codes for '" + dbName + "' found"); return sec; } // Could not find the database among the defined ones. GLogger.debug("SQL error codes for '" + dbName + "' not found"); return new SQLErrorCodes(); } /** * Return {@link SQLErrorCodes} for the given {@link DataSource}, * evaluating "databaseProductName" from the * {@link java.sql.DatabaseMetaData}, or an empty error codes * instance if no <code>SQLErrorCodes</code> were found. * @param dataSource the <code>DataSource</code> identifying the database * @return the corresponding <code>SQLErrorCodes</code> object * @see java.sql.DatabaseMetaData#getDatabaseProductName() */ public SQLErrorCodes getErrorCodes(DataSource dataSource) { synchronized (this.dataSourceCache) { // Let's avoid looking up database product info if we can. SQLErrorCodes sec = this.dataSourceCache.get(dataSource); if (sec != null) { return sec; } // We could not find it - got to look it up. Connection conn = null; try { conn = dataSource.getConnection(); String dbName = (String) conn.getMetaData().getDatabaseProductName();; if (dbName != null) { GLogger.debug("Database product name cached for DataSource [" + dataSource.getClass().getName() + '@' + Integer.toHexString(dataSource.hashCode()) + "]: name is '" + dbName + "'"); sec = getErrorCodes(dbName); this.dataSourceCache.put(dataSource, sec); return sec; } } catch(SQLException e) { DBHelper.close(conn); throw new IllegalStateException("canot getErrorCodes by dataSource",e); } } // Fallback is to return an empty SQLErrorCodes instance. return new SQLErrorCodes(); } public String getDatabaseType(DataSource ds) { Connection conn = null; try { conn = ds.getConnection(); String dbName = (String) conn.getMetaData().getDatabaseProductName(); for (String database : this.errorCodesMap.keySet()) { SQLErrorCodes candidate = errorCodesMap.get(database); if (database.equals(dbName) || PatternMatchHelper.simpleMatch(candidate.getDatabaseProductNames(), dbName)) { return database; } } return null; }catch(SQLException e) { DBHelper.close(conn); throw new IllegalStateException("canot get database type by dataSource",e); } } }