package org.arong.util.jdbc; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.arong.util.FileUtil2; /** * 获取数据库连接、sql执行对象等的工具类 * * @author arong * @since 1.0.0 */ public final class JdbcUtil { /** * 配置信息的集合,在静态代码块中初始化 */ private static Properties prop; /** * 连接池对象 */ private static DataSource dataSource; public static final String CONFIG_FILE_NAME = "arong-db.properties"; public static final String CONFIG_DRIVER_KEY = "driver"; public static final String CONFIG_URL_KEY = "url"; public static final String CONFIG_USER_KEY = "user"; public static final String CONFIG_PASSWORD_KEY = "password"; public static final String CONFIG_USEDATASOURCE_KEY = "useDataSource"; public static final String CONFIG_MAXACTIVE_KEY = "maxActive"; public static final String CONFIG_MAXIDLE_KEY = "maxIdle"; public static final String CONFIG_MINIDLE_KEY = "minIdle"; public static final String CONFIG_INITIALSIZE_KEY = "initialSize"; public static final String CONFIG_MAXWAIT_KEY = "maxWait"; public static final String CONFIG_DATASOURCE_KEY = "dataSource"; /** * 静态代码块,加载配置文件,配置文件放在【项目/WEB-INF/classes】目录下<br> * 如果 */ static { try { prop = new Properties(); File conf = new File(JdbcUtil.class.getResource("/" + CONFIG_FILE_NAME).getPath()); InputStream is = null; if(! conf.exists()){ String[] _p_ = new String[3]; _p_[0] = conf.getAbsolutePath(); conf = new File(FileUtil2.getProjectPath() + "/" + CONFIG_FILE_NAME); if(! conf.exists()){ _p_[1] = conf.getAbsolutePath(); conf = new File(FileUtil2.getAppPath(JdbcUtil.class) + "/" + CONFIG_FILE_NAME); if(! conf.exists()){ is = JdbcUtil.class.getResourceAsStream("/" + CONFIG_FILE_NAME); if(is == null){ _p_[2] = conf.getAbsolutePath(); StringBuffer sb = new StringBuffer(); for(String p : _p_){ sb.append(p).append(";"); } throw new FileNotFoundException(sb.toString()); } } } } // 加载数据库配置文件 if(is != null){ prop.load(is); }else{ prop.load(new BufferedReader(new FileReader(conf))); } // 是否初始化数据源 if (prop.getProperty(CONFIG_USEDATASOURCE_KEY) != null && ("true".equals(prop .getProperty(CONFIG_USEDATASOURCE_KEY) .toLowerCase()) || "yes".equals(prop.getProperty( CONFIG_USEDATASOURCE_KEY).toLowerCase()))) { // 启动数据源 setupDataSource(); } else // 注册数据库驱动 Class.forName(prop.getProperty(CONFIG_DRIVER_KEY)); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException("找不到" + CONFIG_FILE_NAME + "数据库配置文件:" + e.getMessage()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException("找不到" + prop.getProperty(CONFIG_USEDATASOURCE_KEY) + "驱动文件"); } catch (Exception e) { e.printStackTrace(); } } public static DataSource getDataSource() { return dataSource; } /** * 启动连接池 * * @param prop */ private synchronized static void setupDataSource() { // 取得必须属性值 String driver = prop.getProperty(CONFIG_DRIVER_KEY); String url = prop.getProperty(CONFIG_URL_KEY); String user = prop.getProperty(CONFIG_USER_KEY); String password = prop.getProperty(CONFIG_PASSWORD_KEY); // 取得可选属性值 // 最大连接数 String maxActive_str = prop.getProperty(CONFIG_MAXACTIVE_KEY); int maxActive = maxActive_str != null ? Integer.parseInt(maxActive_str) : 0; // 最大峰值 String maxIdle_str = prop.getProperty(CONFIG_MAXIDLE_KEY); int maxIdle = maxIdle_str != null ? Integer.parseInt(maxIdle_str) : 0; // 最小峰值 String minIdle_str = prop.getProperty(CONFIG_MINIDLE_KEY); int minIdle = minIdle_str != null ? Integer.parseInt(minIdle_str) : 0; // 初始化大小 String initialSize_str = prop.getProperty(CONFIG_INITIALSIZE_KEY); int initialSize = initialSize_str != null ? Integer .parseInt(initialSize_str) : 0; // 最大等待时间 String maxWait_str = prop.getProperty(CONFIG_MAXWAIT_KEY); long maxWait = maxWait_str != null ? Long.parseLong(maxWait_str) : 0L; // c3p0连接池 if (prop.getProperty(CONFIG_DATASOURCE_KEY) != null && "c3p0".equals(prop.getProperty(CONFIG_DATASOURCE_KEY) .toLowerCase())) { Object cpds = null; try { cpds = Class.forName( "com.mchange.v2.c3p0.ComboPooledDataSource") .newInstance(); invoked(cpds, "setDriverClass", driver); invoked(cpds, "setJdbcUrl", url); invoked(cpds, "setUser", user); invoked(cpds, "setPassword", password); if (maxActive != 0) invoked(cpds, "setMaxStatements", maxActive); if (maxIdle != 0) invoked(cpds, "setMaxIdleTime", maxIdle); if (minIdle != 0) invoked(cpds, "setMinPoolSize", minIdle); if (initialSize != 0) invoked(cpds, "setInitialPoolSize", initialSize); if (maxWait != 0L) invoked(cpds, "setMaxConnectionAge", (int) maxWait); dataSource = (DataSource) cpds; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException("找不到c3p0所需的jar包,请手动导入!"); } } // dbcp连接池 else { Object bds = null; try { bds = Class.forName("org.apache.commons.dbcp.BasicDataSource") .newInstance(); invoked(bds, "setDriverClassName", driver); invoked(bds, "setUrl", url); invoked(bds, "setUsername", user); invoked(bds, "setPassword", password); if (maxActive != 0) invoked(bds, "setMaxActive", maxActive); if (maxIdle != 0) invoked(bds, "setMaxIdle", maxIdle); if (minIdle != 0) invoked(bds, "setMinIdle", minIdle); if (initialSize != 0) invoked(bds, "setInitialSize", initialSize); if (maxWait != 0L) invoked(bds, "setMaxWait", maxWait); dataSource = (DataSource) bds; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException("找不到dbcp所需的jar包,请手动导入!"); } } } /** * 根据方法名调用给定对象的方法 * * @param obj * @param methodName * @param value */ private static void invoked(Object obj, String methodName, Object value) { Class<?> clazz = obj.getClass(); Method[] methods = clazz.getDeclaredMethods(); String _methodName = null; try { for (int i = 0; i < methods.length; i++) { _methodName = methods[i].getName(); if (_methodName.equals(methodName)) { methods[i].invoke(obj, value); } } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } /** * 获取Connection对象 * * @return */ public static Connection getConnection() { Connection connection = null; try { /* * if (dataSource != null) { connection = * dataSource.getConnection(); } else { connection = * DriverManager.getConnection( prop.getProperty("url"), prop); } */ connection = dataSource != null ? dataSource.getConnection() : DriverManager .getConnection(prop.getProperty("url"), prop); } catch (SQLException e) { e.printStackTrace(); } return connection; } /** * 获取PreparedStatement对象 * * @param sql * @return */ public static PreparedStatement getPstmt(Connection conn, String sql) { PreparedStatement pstmt = null; if (conn != null && sql != null) { try { pstmt = conn.prepareStatement(sql); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("获取PreparedStatement异常"); } } return pstmt; } /** * 获取PreparedStatement对象 * * @param conn * 数据库连接对象 * @param sql * sql语句字符串 * @param params * sql语句中占位符所对应的参数数组 * @return */ public static PreparedStatement getPstmt(Connection conn, String sql, Object[] params) { PreparedStatement pstmt = null; if (conn != null && sql != null) { try { pstmt = conn.prepareStatement(sql); if (params != null) { Integer length = params.length; // 循环占位符数组对象 for (int i = 0; i < length; i++) { pstmtSetValue(pstmt, params[i], i + 1); } } } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("获取PreparedStatement异常"); } } return pstmt; } /** * 执行PreparedStatement的set方法为占位符设值 * * @param pstmt * @param value * @param index */ public static void pstmtSetValue(PreparedStatement pstmt, Object value, int index) { if (pstmt != null) { try { pstmt.setObject(index, value); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("PreparedStatement设置占位符异常"); } } } /** * 获取Statement对象 * * @param conn * @return */ public static Statement getStmt(Connection conn) { Statement stmt = null; if (conn != null) { try { stmt = conn.createStatement(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("获取Statement异常"); } } return stmt; } /** * Connection关闭连接 */ public static void close(Connection conn) { if (conn != null) { try { conn.close(); conn = null; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Connection 关闭异常"); } } } /** * 关闭连接 * * @param pstmt */ public static void close(PreparedStatement pstmt) { if (pstmt != null) { try { pstmt.close(); pstmt = null; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("PreparedStatement 关闭异常"); } } } /** * 关闭连接 * * @param pstmt */ public static void close(Statement stmt) { if (stmt != null) { try { stmt.close(); stmt = null; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Statement 关闭异常"); } } } /** * 关闭连接 * * @param rs */ public static void close(ResultSet rs) { if (rs != null) { try { rs.close(); rs = null; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("ResultSet 关闭异常"); } } } /** * 关闭连接 * * @param pstmt * @param conn */ public static void close(PreparedStatement pstmt, Connection conn) { close(pstmt); close(conn); } /** * 关闭连接 * * @param rs * @param pstmt * @param conn */ public static void close(ResultSet rs, PreparedStatement pstmt, Connection conn) { close(rs); close(pstmt); close(conn); } /** ------------------------------事务相关---------------------------- **/ /** * 本地线程,用于存储当前事务公用的Connection对象 */ private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); /** * 通过在本地线程中获取数据库连接对象 * * @return */ public static Connection getConnectionInThreadLocal() { Connection conn = tl.get(); if (conn == null) { conn = getConnection(); tl.set(conn); } return conn; } /** * 开启事务 */ public static void startTransaction() { Connection conn = getConnectionInThreadLocal(); if (conn != null) { try { conn.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Connection 开启事务异常"); } } } /** * 开启事务 * * @param isolation * 事务隔离级别<br > * 取值为Connection中的4个常量<br > * 取0时不设置,采用所使用的数据库系统的默认值 */ public static void startTransaction(int isolation) { Connection conn = getConnectionInThreadLocal(); if (conn != null) { try { conn.setAutoCommit(false); if (isolation != 0) conn.setTransactionIsolation(isolation); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Connection 开启事务异常"); } } } /** * 事务回滚 */ public static void rollback() { Connection conn = tl.get(); if (conn != null) { try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Connection 事务回滚异常"); } } } /** * 事务提交 */ public static void commit() { Connection conn = tl.get(); if (conn != null) { try { conn.commit(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("Connection 事务提交异常"); } } } /** * 事务结束后关闭连接对象并移除本地线程中的连接对象 */ public static void closeAndRemove() { Connection conn = tl.get(); if (conn != null) { tl.remove(); close(conn); } } /** * 提交事务,移除连接,关闭连接 */ public static void commit_Close_Remove() { Connection conn = tl.get(); if (conn != null) { try { conn.commit(); tl.remove(); close(conn); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("事务提交、关闭、清空异常"); } } } /** * 获取本工具类的配置说明信息 */ public static void getConfigIntru() { System.out.println("一、在工程的src目录下创建一个名为:" + CONFIG_FILE_NAME + "的数据库配置文件。"); System.out.println(""); System.out.println(" 这个文件有4个必需属性:"); System.out.println(" 1." + CONFIG_DRIVER_KEY + ":数据库驱动"); System.out.println(" 2." + CONFIG_URL_KEY + ":数据库url地址"); System.out.println(" 3." + CONFIG_USER_KEY + ":数据库用户名称"); System.out.println(" 4." + CONFIG_PASSWORD_KEY + ":数据库用户密码"); System.out.println(""); System.out.println(" 这个文件有7个可选属性:"); System.out.println(" 5." + CONFIG_USEDATASOURCE_KEY + ":是否使用数据源"); System.out.println(" 6." + CONFIG_MAXACTIVE_KEY + ":数据源最大连接数"); System.out.println(" 7." + CONFIG_MAXIDLE_KEY + ":数据源最大峰值"); System.out.println(" 8." + CONFIG_MINIDLE_KEY + ":数据源最小峰值"); System.out.println(" 9." + CONFIG_INITIALSIZE_KEY + ":数据源初始值"); System.out.println(" 10." + CONFIG_MAXWAIT_KEY + ":最大等待时间"); System.out.println(" 11." + CONFIG_DATASOURCE_KEY + ":数据源的名称"); System.out.println(""); System.out.println("二、如果使用数据源,请指定" + CONFIG_USEDATASOURCE_KEY + "=true或yes"); System.out.println(""); System.out.println(" 当" + CONFIG_DATASOURCE_KEY + "=c3p0,则使用c3p0连接池;"); System.out.println(" 当" + CONFIG_DATASOURCE_KEY + "属性没有指定值,默认使用dbcp连接池。"); } }