阿里巴巴的開源數據庫連接池工具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 }