jdbc连接数据库存在着大批量用户进行短时间的SQL连接操作的 需求,而普通用户连接后直接断开与数据库的连接,下次连接需要重新建立桥梁,再频繁访问时。这是很消耗性能的一个操作,因此诞生了数据库连接池技术。提前创建 一些连接,避免频繁的创建连接,并且可以管理程序和数据库之间的连接,动态分配桥梁给申请连接的应用程序。
@Test
public void test1() throws SQLException {
DruidDataSource ds = new DruidDataSource();
//设置基本参数
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/atguigu");
ds.setUsername("root");
ds.setPassword("123456");
//设置其他连接参数
ds.setInitialSize(10);//初始化连接数
ds.setMaxActive(11);//最大活动连接数
ds.setMaxWait(1000);//最大等待时间
ds.setMinIdle(5);//最小连接数
for (int i = 0; i < 30; i++) {
DruidPooledConnection conn = ds.getConnection();
System.out.println("conn = " + conn);
conn.close();//释放连接到连接池中
}
}
Druid连接池提供了一个createDataSource(Properties prop)的方法来统一设置连接池的参数,由于底层是采用硬编码编写的,所以配置文件的变量名称必须与代码一致,否则无法将配置读取到项目中。
具体步骤:
public class JdbcTool2 {
private static DataSource ds;
static {
//1.注册驱动
Properties prop = new Properties();
try {
//加载配置文件中的数据到Properties中
//配置文件要放到src目录下
prop.load(JdbcTools.class.getClassLoader().getResourceAsStream("jdbc.properties"));
//批量设置参数的大小
ds = DruidDataSourceFactory.createDataSource(prop);
//Class.forName(className);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException {
//获取连接
return ds.getConnection();
}
}
在MVC分层模型中,如果我们在DAO层中使用jdbc技术建立连接修改了账号表的数据,而在业务层中为了模拟转账的需求又再次获取了一个Connection对象来实现该事务。该写法会导致我们重复创建了不同的连接,并且当代码出现异常执行事务回滚时会发现不能恢复到原有的状态。这是由于两层代码中的连接对象并不是同一个。
解决方案如下:
public class JdbcTool3 {
private static DataSource ds;
private static ThreadLocal<Connection> threadLocal;
static {
//1.注册驱动
Properties prop = new Properties();
try {
//加载配置文件中的数据到Properties中
prop.load(JdbcTools.class.getClassLoader().getResourceAsStream("jdbc.properties"));//配置文件要放到src目录下
//批量设置参数的大小
ds = DruidDataSourceFactory.createDataSource(prop);
//Class.forName(className);
//3.创建线程
threadLocal = new ThreadLocal<>();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException {
//获取连接
Connection conn = threadLocal.get();
if(conn==null){
conn = ds.getConnection();
threadLocal.set(conn);
}
return conn;
}
public static void closeAll(ResultSet rs, Statement st, Connection conn){
try {
if(rs!=null){
rs.close();
}
if(st!=null){
st.close();
}
if(conn!=null){
conn.close();//将连接还给连接池
threadLocal.remove();//把连接对象从当前线程移除
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static DataSource getDs(){
return ds;
}
}
示例代码
一个已经封装好了的工具类,可以简化我们在编写DAO层代码的复杂度。主要分两个方面来介绍一下该工具类,分别是增删改和查询操作。
简化之前的代码:
//添加用户
public int addUser(User user) throws SQLException {
Connection conn = JdbcTool3.getConnection();
PreparedStatement pst = conn.prepareStatement("insert into users(username, password) values(?,?)");
pst.setObject(1,user.getUsername());
pst.setObject(2,user.getPassword());
int i = pst.executeUpdate();
JdbcTool3.closeAll(null,pst,conn);
return i;
}
使用DBUtils后的代码:
//添加用户
public int addUser(User user) throws SQLException {
String sql = "insert into users(username, password) values(?,?)";
int update = qr.update(sql, user.getUsername(), user.getPassword());
return update;
}
简化之前的代码:
//根据id查询用户
public User getUserById(int id) throws SQLException {
Connection conn = JdbcTool3.getConnection();
PreparedStatement pst = conn.prepareStatement("select id, username, password from users where id = ?");
pst.setObject(1,id);
ResultSet rs = pst.executeQuery();
User user = null;
if(rs.next()){
int ID = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
System.out.print("ID = " + ID);
System.out.print(",username = " + username);
System.out.println(",password = " + password);
user = new User(ID, username, password);
}
JdbcTool3.closeAll(null,pst,conn);
return user;
}
使用DBUtils后的代码:
public User getUserById2(int id) throws SQLException {
String sql = "select id, username, password from users where id = ?";
return qr.query(sql,new BeanHandler<>(User.class),id);
}
其中query方法中的BeanHandler对象底层是通过反射的方式来动态的将对象创建出来,可以选择相应容器来包装,如果是单个字段的话,可以使用相应的Handle对象来处理。