1. 下载Druid连接池
1.1 下载源码
https://github.com/alibaba/druid
点击Download ZIP下载源码,如果遇到墙住的情况,可以挂个加速器。
1.2 下载jar包
https://repo1.maven.org/maven2/com/alibaba/druid/
2. 引用jar包,添加为库文件
拷贝到lib文件夹后,点击右键->添加为库文件
3. 使用Druid
3.1 使用硬编码方式
public void DruidHard() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://192.168.101.130:3306/atguigu");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
Connection connection = druidDataSource.getConnection();
// JDBC中的标准操作步骤
String sel_sql = "SELECT * FROM t_bank";
PreparedStatement preparedStatement = connection.prepareStatement(sel_sql);
ResultSet resultSet = preparedStatement.executeQuery();
List<Map> list = new ArrayList<Map>();
ResultSetMetaData metaData = preparedStatement.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
Map<String,Object> map = new HashMap<String,Object>();
for (int i = 1; i < columnCount; i++) {
map.put(metaData.getColumnLabel(i), resultSet.getObject(i));
}
list.add(map);
}
System.out.println("DruidHard" + list);
preparedStatement.close();
connection.close();
}
3.2 使用配置文件方式
druid.properties文件内容:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.101.130:3306/atguigu
username=root
password=root
defaultAutoCommit=false
defaultTransactionIsolation=REPEATABLE_READ
maxActive=10
initialSize=3
public void DruidSoft() throws Exception {
Properties properties = new Properties();
InputStream resourceAsStream = DruidUserPart.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();
String sel_sql = "SELECT * FROM t_bank;";
PreparedStatement preparedStatement = connection.prepareStatement(sel_sql);
ResultSet resultSet = preparedStatement.executeQuery();
List<Map> list = new ArrayList<Map>();
ResultSetMetaData metaData = preparedStatement.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
Map<String,Object> map = new HashMap<String,Object>();
for (int i = 1; i < columnCount; i++) {
map.put(metaData.getColumnLabel(i), resultSet.getObject(i));
}
list.add(map);
}
System.out.println("DruidSoft" + list);
resultSet.close();
connection.close();
}
测试方法:
@Test
public void test() throws Exception {
DruidHard();
DruidSoft();
}
结果显式:
3.3 Druid常用配置
配置 | 缺省 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) | |
url | 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter | |
driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下) | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
timeBetweenEvictionRunsMillis | 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明 | |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun | |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 | |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall | |
proxyFilters | 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
3.4 连接池工具类及使用案例
结构思想
将注册服务、建立连接、回收资源等标准操作封装成静态方法,供服务层调用
第一版本
package com.untifa.api.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @Title: JdbcUtil
* @Package: com.untifa.api.utils
* @Description:
* @Author: untifa
* @Date: 2023/3/25 - 19:52
*/
public class JdbcUtilV1 {
private static DataSource dataSource = null;
static {
Properties properties = new Properties();
InputStream resourceAsStream = JdbcUtilV1.class.getClassLoader().getResourceAsStream("druid.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getconnection() throws SQLException {
return dataSource.getConnection();
}
public static void freeconnection(Connection connection) throws SQLException {
connection.close();
}
}
**思考:**
https://blog.csdn.net/FuTian0715/article/details/129699894
文章4.10中提到的转账例子,如何让服务层与Dao层等同一个线程的不同方法获取到相同的连接?
这就需要使用到ThreadLocal了,ThreadLocal用于保存线程的共享变量,原因是在Java中,每一个线程对象中都有一个ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。 1、ThreadLocal对象.get: 获取ThreadLocal中当前线程共享变量的值。 2、ThreadLocal对象.set: 设置ThreadLocal中当前线程共享变量的值。 3、ThreadLocal对象.remove: 移除ThreadLocal中当前线程共享变量的值。
第二版本
package com.untifa.api.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @Title: JdbcUtil
* @Package: com.untifa.api.utils
* @Description:
* @Author: untifa
* @Date: 2023/3/25 - 19:52
*/
public class JdbcUtilV2 {
private static DataSource dataSource = null;
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
static {
Properties properties = new Properties();
InputStream resourceAsStream = JdbcUtilV2.class.getClassLoader().getResourceAsStream("druid.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getconnection() throws SQLException {
Connection connection = tl.get();
if (connection == null) {
connection = dataSource.getConnection();
tl.set(connection);
}
return connection;
}
public static void free() throws SQLException {
Connection connection = tl.get();
if (connection != null) {
tl.remove();
}
connection.setAutoCommit(true);
connection.close();
}
}
案例:
转账服务类
package com.untifa.api.transactionnew;
import com.untifa.api.utils.JdbcUtilV2;
import org.junit.Test;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @Title: BankService
* @Package: com.untifa.api.transaction
* @Description:
* @Author: untifa
* @Date: 2023/3/23 - 23:32
*/
public class BankService {
public void transfer(String addAcount, String subAccount, BigDecimal money) throws ClassNotFoundException, SQLException {
Connection connection = JdbcUtilV2.getconnection();
boolean flag = false; // 转账成功标志
try {
connection.setAutoCommit(false);
BankDao bankDao = new BankDao();
bankDao.addmoney(addAcount, money);
bankDao.submoney(subAccount, money);
flag = true;
connection.commit();
} catch (SQLException e) {
flag = false;
connection.rollback();
System.out.println(":#####" + e.getMessage());
e.printStackTrace();
throw e;
} finally {
JdbcUtilV2.free();
if (flag) {
System.out.println("转账成功");
} else {
System.out.println("转账失败");
}
}
}
@Test
public void testTransfer() throws SQLException, ClassNotFoundException {
String addAcount = "ergouzi";
String subAccount = "lvdandan";
BigDecimal money = new BigDecimal("500");
transfer(addAcount, subAccount, money);
}
}
Dao类
package com.untifa.api.transactionnew;
import com.untifa.api.utils.JdbcUtilV2;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @Title: BankDao
* @Package: com.untifa.api.statement
* @Description:
* @Author: untifa
* @Date: 2023/3/23 - 23:27
*/
public class BankDao {
public void addmoney(String account, BigDecimal money) throws SQLException {
Connection connection = JdbcUtilV2.getconnection();
String upd_sql = "update t_bank t set t.money = t.money + ? where t.account = ?";
PreparedStatement preparedStatement = connection.prepareStatement(upd_sql);
preparedStatement.setObject(1, money);
preparedStatement.setObject(2, account);
int i = preparedStatement.executeUpdate();
if (i > 0) {
System.out.println("加钱成功");
}
preparedStatement.close();
}
public void submoney(String account, BigDecimal money) throws SQLException {
Connection connection = JdbcUtilV2.getconnection();
String upd_sql = "update t_bank t set t.money = t.money - ? where t.account = ?";
PreparedStatement preparedStatement = connection.prepareStatement(upd_sql);
preparedStatement.setObject(1, money);
preparedStatement.setObject(2, account);
int i = preparedStatement.executeUpdate();
if (i > 0) {
System.out.println("减钱成功");
}
preparedStatement.close();
}
}
3.5 Dao方法封装案例
BaseDao.java
package com.untifa.api.utils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Title: BaseDao
* @Package: com.untifa.api.utils
* @Description:
* @Author: untifa
* @Date: 2023/3/25 - 21:23
*/
public abstract class BaseDao {
/**
* 非DQL语句
* @param sql
* @param params
* @return
* @throws SQLException
*/
public int Update(String sql, Object... params) throws SQLException {
Connection connection = JdbcUtilV2.getconnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
System.out.println(params[i]);
preparedStatement.setObject(i + 1, params[i]);
}
int rows = preparedStatement.executeUpdate();
preparedStatement.close();
if (connection.getAutoCommit()) {
JdbcUtilV2.free();
}
return rows;
}
/**
* DQL语句
* 涉及到泛型、反射等技术的使用
* @param clazz
* @param sql
* @param params
* @return
* @param <T>
* @throws Exception
*/
public <T> List<T> Query(Class<T> clazz, String sql, Object... params) throws Exception {
Connection connection = JdbcUtilV2.getconnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
System.out.println(params[i]);
preparedStatement.setObject(i + 1, params[i]);
}
ArrayList<T> list = new ArrayList<T>();
ResultSet resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = preparedStatement.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
T t = clazz.newInstance();
for (int i = 1; i <= columnCount; i++) {
Field field = clazz.getDeclaredField(metaData.getColumnLabel(i));
field.setAccessible(true);
System.out.println("111\t" + field.toString());
System.out.println("222\t" +resultSet.getObject(i));
field.set(t,resultSet.getObject(i));
}
list.add(t);
}
resultSet.close();
preparedStatement.close();
if (connection.getAutoCommit()) {
JdbcUtilV2.free();
}
return list;
}
}
JavaBean对应的类t_bank.java
package com.untifa.api.utils;
import java.math.BigDecimal;
/**
* @Title: t_user
* @Package: com.untifa.api.utils
* @Description:
* @Author: untifa
* @Date: 2023/3/25 - 22:58
*/
public class t_bank {
private int id;
private String account;
private BigDecimal money;
public t_bank() {
}
public t_bank(int id, String account, BigDecimal money) {
this.id = id;
this.account = account;
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
t_bank t_bank = (t_bank) o;
if (id != t_bank.id) return false;
if (!account.equals(t_bank.account)) return false;
return money.equals(t_bank.money);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + account.hashCode();
result = 31 * result + money.hashCode();
return result;
}
@Override
public String toString() {
return "t_bank{" +
"id=" + id +
", account='" + account + '\'' +
", money=" + money +
'}';
}
}
测试业务类JdbcCurdPart.java
package com.untifa.api.utils;
import org.junit.Test;
import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Title: JdbcCurdPart
* @Package: com.untifa.api.utils
* @Description:
* @Author: untifa
* @Date: 2023/3/25 - 20:13
*/
public class JdbcCurdPart extends BaseDao{
@Test
public void sel_test() throws Exception {
Connection connection = JdbcUtilV2.getconnection();
String sel_sql = "SELECT * FROM t_bank";
List<t_bank> list = Query(t_bank.class, sel_sql);
System.out.println("####" + list);
connection.commit();
JdbcUtilV1.freeconnection(connection);
}
@Test
public void upd_test() throws SQLException {
Connection connection = JdbcUtilV2.getconnection();
String upd_sql = "UPDATE t_bank t SET t.money = ? ;";
int update = Update(upd_sql, BigDecimal.valueOf(20000));
System.out.println("影响行数:" + update);
connection.commit();
}
}
sel_test()测试结果
upd_test()测试结果
注意:
在测试查询的过程中报错,这是因为JavaBean中的money类型与数据库中money类型不匹配导致,转换时会报异常,将数据库中money的类型改为DECIMAL类型后,查询就不在报错了。