不用Spring如何正确使用Druid连接池数据源
阿里巴巴的开源数据库连接池工具Druid数据源,在实际使用的时候,通常会和Spring或SpringBoot整合一起使用。
但有些时候如果不用使用Spring或SpringBoot,而直接使用Druid的原生API来操作数据库的话,该如何正确使用呢?
下面是一个使用Druid连接池简单操作数据库的工具类,重点关注Druid数据源的创建和复用:
1 /** 2 * 数据库操作工具类 3 * 4 * @author shiyanjun 5 * @since 2020/01/05 6 */ 7 @Slf4j 8 public class DbUtil { 9 private static final String DB_URL = "jdbc:mysql://localhost:3306/authapi"; // 数据库连接URL 10 private static final String DB_USERNAME = "root"; // 数据库用户名 11 private static final String DB_PASSWORD = "123456"; // 数据库密码 12 13 // Druid数据源,全局唯一(只创建一次) 14 private static DruidDataSource druidDataSource; 15 16 /** 17 * 执行SQL更新 18 * 19 * @param updateSql 20 * @throws SQLException 21 */ 22 public static void insert(String updateSql) throws SQLException { 23 Connection connection = null; 24 Statement statement = null; 25 ResultSet resultSet = null; 26 try { 27 connection = getDruidConnection(); 28 statement = connection.createStatement(); 29 int count = statement.executeUpdate(updateSql); 30 log.info(">>>>>>>>>>>>> 插入数据 {}", count); 31 } finally { 32 // 切记!!! 一定要释放资源 33 closeResource(connection, statement, resultSet); 34 } 35 } 36 37 /** 38 * 执行SQL查询 39 * 40 * @param querySql 41 * @return 42 * @throws Exception 43 */ 44 public static List<Map<String, Object>> executeQuery(String querySql) throws Exception { 45 List<Map<String, Object>> resultList = new ArrayList<>(); 46 Connection connection = null; 47 Statement statement = null; 48 ResultSet resultSet = null; 49 try { 50 connection = getDruidConnection(); 51 statement = connection.createStatement(); 52 resultSet = statement.executeQuery(querySql); 53 ResultSetMetaData metaData = resultSet.getMetaData(); 54 while (resultSet.next()) { 55 int columnCount = metaData.getColumnCount(); 56 Map<String, Object> resultMap = new LinkedHashMap<>(); 57 for (int i = 1; i <= columnCount; i++) { 58 String columnName = metaData.getColumnName(i);// 字段名称 59 Object columnValue = resultSet.getObject(columnName);// 字段值 60 resultMap.put(columnName, columnValue); 61 } 62 resultList.add(resultMap); 63 } 64 log.info(">>>>>>>>>>>> 查询数据:{}", resultList); 65 } finally { 66 // 切记!!! 一定要释放资源 67 closeResource(connection, statement, resultSet); 68 } 69 return resultList; 70 } 71 72 /** 73 * 获取Druid数据源 74 * 75 * @return 76 * @throws SQLException 77 */ 78 private static DruidDataSource getDruidDataSource() throws SQLException { 79 // 保证Druid数据源在多线程下只创建一次 80 if (druidDataSource == null) { 81 synchronized (DbUtil.class) { 82 if (druidDataSource == null) { 83 druidDataSource = createDruidDataSource(); 84 return druidDataSource; 85 } 86 } 87 } 88 log.info(">>>>>>>>>>> 复用Druid数据源:url={}, username={}, password={}", 89 druidDataSource.getUrl(), druidDataSource.getUsername(), druidDataSource.getPassword()); 90 return druidDataSource; 91 } 92 93 /** 94 * 创建Druid数据源 95 * 96 * @return 97 * @throws SQLException 98 */ 99 private static DruidDataSource createDruidDataSource() throws SQLException { 100 DruidDataSource druidDataSource = new DruidDataSource(); 101 druidDataSource.setUrl(DB_URL); 102 druidDataSource.setUsername(DB_USERNAME); 103 druidDataSource.setPassword(DB_PASSWORD); 104 105 /*----下面的具体配置参数自己根据项目情况进行调整----*/ 106 druidDataSource.setMaxActive(20); 107 druidDataSource.setInitialSize(1); 108 druidDataSource.setMinIdle(1); 109 druidDataSource.setMaxWait(60000); 110 111 druidDataSource.setValidationQuery("select 1 from dual"); 112 113 druidDataSource.setTimeBetweenEvictionRunsMillis(60000); 114 druidDataSource.setMinEvictableIdleTimeMillis(300000); 115 116 druidDataSource.setTestWhileIdle(true); 117 druidDataSource.setTestOnBorrow(false); 118 druidDataSource.setTestOnReturn(false); 119 120 druidDataSource.setPoolPreparedStatements(true); 121 druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(20); 122 123 druidDataSource.init(); 124 log.info(">>>>>>>>>>> 创建Druid数据源:url={}, username={}, password={}", 125 druidDataSource.getUrl(), druidDataSource.getUsername(), druidDataSource.getPassword()); 126 return druidDataSource; 127 } 128 129 /** 130 * 获取Druid连接 131 * 132 * @return 133 * @throws SQLException 134 */ 135 private static DruidPooledConnection getDruidConnection() throws SQLException { 136 DruidDataSource druidDataSource = getDruidDataSource(); 137 DruidPooledConnection connection = druidDataSource.getConnection(); 138 return connection; 139 } 140 141 /** 142 * 释放资源 143 * 144 * @param connection 145 * @param statement 146 * @param resultSet 147 * @throws SQLException 148 */ 149 private static void closeResource(Connection connection, 150 Statement statement, ResultSet resultSet) throws SQLException { 151 // 注意资源释放顺序 152 if (resultSet != null) { 153 resultSet.close(); 154 } 155 if (statement != null) { 156 statement.close(); 157 } 158 if (connection != null) { 159 connection.close(); 160 } 161 } 162 }
一个简单的测试类,模拟多线程向数据库表中插入1w条数据:
1 import java.text.SimpleDateFormat; 2 import java.util.Date; 3 import java.util.UUID; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 7 /** 8 * CREATE TABLE `auth_code` ( 9 * `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键', 10 * `auth_code` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '授权码', 11 * `status` varchar(255) CHARACTER SET utf8mb4 NOT NULL DEFAULT '1' COMMENT '状态', 12 * `remark` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '备注', 13 * `create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期', 14 * `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改日期', 15 * PRIMARY KEY (`id`) 16 * ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=latin1; 17 */ 18 public class AppTest { 19 private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 20 private static ExecutorService executorService = Executors.newCachedThreadPool();// 线程池 21 22 public static void main(String[] args) throws Exception { 23 String INSERT_SQL = "INSERT INTO `authapi`.`auth_code` " + 24 "(`auth_code`, `status`, `remark`, `create_date`, `update_time`) VALUES ('%s',%s,'%s','%s','%s')"; 25 // 模拟多线程向数据库插入1万条数据 26 for (int i = 0; i < 10000; i++) { 27 Thread.sleep(5); 28 String code = UUID.randomUUID().toString().replaceAll("-", ""); 29 String dateStr = dateFormat.format(new Date()); 30 String formatSql = String.format(INSERT_SQL, code, i, code, dateStr, dateStr); 31 executorService.execute(() -> { 32 try { 33 DbUtil.insert(formatSql); 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 }); 38 } 39 } 40 }