JDBC操作技术和连接池详解

1、JDBC连接Orcale数据库

<html>
<head>
<title>使用JDBC直接访问ORACLE数据库</title>
</head>
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.sql.*"%>
<%--
	使用JDBC连接Oracle数据库
	使用ORCL数据库
	用户名:scott
	密码:tiger
	访问表 t_user
--%>
<body>
<center><h2>
使用JDBC直接访问Oracle数据库
</h2></center>
<center><h3>
欢迎连接Oracle!
</h3></center>
<table border=1 align="center">
<tr>
<td>用户id</td>
<td>用户姓名</td>
<td>用户密码</td>
<td>用户标识</td>
<td>账户余额</td>
</tr>

<%!
	String DBDRIVER = "oracle.jdbc.driver.OracleDriver" ;
	String DBURL = "jdbc:oracle:thin:@localhost:1521:ORCL" ;
	String DBUSER = "scott" ;
	String DBPASSWORD = "tiger" ;
	Connection conn = null ;
	Statement stmt = null ;
%>
   <%
	try
	{
		Class.forName(DBDRIVER) ;
		// 连接时必须填写用户名及密码
		conn = DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD) ;
		// 创建表的SQL语句
		stmt = conn.createStatement() ;
		ResultSet rs=stmt.executeQuery("select * from t_user");
		while(rs.next())
        {
    %>
    <tr>
      <td><%=rs.getInt("user_id")%></td>
      <td><%=rs.getString("user_name")%></td>
      <td><%=rs.getString("user_password")%></td>
      <td><%=rs.getInt("user_flag")%></td>
      <td><%=rs.getDouble("balance")%></td>
   </tr>
<%	
}
		rs.close();
		stmt.close();
		conn.close();
	}
	catch(Exception e)
	{
		out.println(e) ;
	}
%>
</table>
</body>
</html>

步骤:

  • 数据库配置
	//数据库驱动
	String DBDRIVER = "oracle.jdbc.driver.OracleDriver" ;
	//数据库连接url
	String DBURL = "jdbc:oracle:thin:@localhost:1521:ORCL" ;
	//数据库用户名
	String DBUSER = "scott" ;
	//密码
	String DBPASSWORD = "tiger" ;
  • 通过jdbc操作Orcale数据库查询数据
		
		//反射。通过类名,加载驱动的类,创建反射对象
		Class.forName(DBDRIVER) ;
		// 连接时必须填写用户名及密码。获取连接
		conn = DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD) ;
		// 创建表的SQL语句
		stmt = conn.createStatement() ;
		//查询到数据
		ResultSet rs=stmt.executeQuery("select * from t_user");
  • jsp从查询数据中获取各个字段
  <td><%=rs.getInt("user_id")%></td>
      <td><%=rs.getString("user_name")%></td>
      <td><%=rs.getString("user_password")%></td>
      <td><%=rs.getInt("user_flag")%></td>
      <td><%=rs.getDouble("balance")%></td>

2、JDBC连接SQL Server数据库

<html> 
<head>
<title>使用JSP访问SqlServer2000数据库 </title>
</head> 
<%@ page contentType="text/html;charset=gb2312"%> 
<%@ page import="java.sql.*"%> 
<%--
	使用JDBC连接SqlServer数据库
	使用hsconnect数据库
    使用t_user_b表
	用户名:sa
	密码:  
--%>
<body>  
<h2>
通过jdbc驱动访问SQL数据库
</h2>
<table border=1 >
  <tr>
    <td>用户帐号</td>
    <td>用户角色</td>
    <td>用户密码</td>
  </tr>
<%
   Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); 
   String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=hsconnect"; 
    String user="sa"; 
    String password=""; 
    Connection conn= DriverManager.getConnection(url, user, password);  
   Statement stmt=conn.createStatement( ); 
   String sql="select * from t_user_b"; 
   ResultSet rs=stmt.executeQuery(sql);
 while(rs.next()) 
{%> 
   <tr>
    <td><%=rs.getString("account")%></td>
    <td><%=rs.getString("roleid")%></td>
    <td><%=rs.getString("password")%></td>
  </tr>
<%
}%> 
<%
   rs.close(); 
   stmt.close(); 
   conn.close(); 
%> 
</body> 
</html> 


步骤也是一样的

  • 引入java.sql包

  • 先配置驱动、url、用户名和密码等。

  • 然后获取连接,设计SQL语句,查询数据。

  • 从查询数据中拿到需要的字段

3、JDBC操作MySQL数据库

<html>
<head>
<title>使用JDBC直接访问MySQL数据库</title>
</head>
<%@ page contentType="text/html;charset=GB2312" %>
<%@ page import="java.sql.*" %>
<%--
	使用JDBC连接MySQL数据库
	使用hibernate_basemapping数据库
    使用t_user3表
	用户名:root
	密码:mysql 
--%>
<body>
<center><h2>
使用JDBC直接访问MySQL数据库
</h2></center>
<center><h3>
欢迎连接MySQL!
</h3></center>
<table border=1 align="center">
<tr>
<td>用户id</td>
<td>用户姓名</td>
<td>用户密码</td>
</tr>
<%
Class.forName("org.gjt.mm.mysql.Driver");
Connection con=DriverManager.getConnection("jdbc:mysql://127.0.0.1/hibernate_basemapping","root","mysql");
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select * from t_user3");
while(rs.next())
{
%>
<tr>
<td><%=rs.getString("user_id")%></td>
<td><%=rs.getString("name")%></td>
<td><%=rs.getString("password")%></td>
</tr>
<%	
}
rs.close();
stmt.close();
con.close();
%>
</table>
</body>
</html>

还是一样的步骤;

加载驱动(创建反射对象),配置url,用户名和密码。获取连接,写SQL语句,查询数据

Class.forName("org.gjt.mm.mysql.Driver");
Connection con=DriverManager.getConnection("jdbc:mysql://127.0.0.1/hibernate_basemapping","root","mysql");
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select * from t_user3");

jsp中获取查询出来数据的字段

<td><%=rs.getString("user_id")%></td>
<td><%=rs.getString("name")%></td>
<td><%=rs.getString("password")%></td>

get字段数据类型(字段名);

可以看到,JDBC不论是操作哪种数据库,步骤都是一样的。区别就在于各个数据库的驱动、连接地址不一样!

4、使用连接池连接数据库

4.1、连接池原理

1、连接池技术的核心思想就是:连接复用

通过建立一个数据库连接池以及一套连接使用、分配、管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免数据库连接频繁建立、关闭的开销 。(如果没有连接池,那我们每请求连接一次数据库都要创建一个连接,并且这个连接得不到复用,连接关闭就直接销毁)

2、连接池主要由三部分组成:连接池的建立、连接池中连接的使用管理、连接池的关闭

  • 连接池的建立

应用程序中建立的连接池其实是一个静态的,所谓静态连接池是指连接池中的连接在系统初始化就已分配好,且不能随意关闭连接。通过读取连接属性文件Connections.properties(里面放连接驱动、url、用户、密码等数据库配置信息)与数据库实例建立连接。

  • 连接池的管理

场景:“小人过河”。

请求连接池中的连接一共会有四种情况。

第一种 ,客户请求数据库连接时,容器中存在空闲连接(就是已经创建好的连接但是没有被使用,复用),那么直接把连接分配给客户;

第二种 ,客户请求数据库连接时,容器中没有空闲连接,这时候需要判断当前连接数是否达到设定的最大连接数maxCount,如果没有达到,就创建一个新的连接分配给客户;

第三种 ,如果判断当前连接数已经大于设定的最大连接数,就会让客户进入等待队列,按设定的maxWaitTime进行等待,等待期间有空闲连接就会分配给等待连接的请求。

连接池中的等待队列一般是一个先进先出的队列,也就是说,如果当前连接数达到了连接池的最大连接数,新的连接请求会被放到等待队列的末尾,等待前面的连接请求被处理完毕后再进行连接。在队列中,连接请求按照先到先服务的原则进行连接,也就是说,先到达的连接请求会先被处理,后到达的连接请求会被放到队列的末尾等待处理。当连接池中有可用的连接时,连接池会从队列中取出最先到达的连接请求进行连接。

第四种 ,如果等待队列中的等待连接请求在设定的最大等待时间内仍然没有分配到连接,就会抛出无空闲连接的异常给客户。(会报栈内存溢出异常)

  • 当应用程序退出时,应关闭连接池。此时应把在连接池建立时向数据库申请的连接对象统一归还给数据库(关闭所有数据库连接)。

3、深入

  • 连接好比小船,而连接池就是港湾(放小船的地方)。连接池一创建就会维护一定数量的连接,小船是本来就有的,当有请求来时,有小船空闲就可以直接拿。

  • 连接池的作用是管理连接的生命周期,包括创建、维护和释放连接。

  • 连接池并不是在连接请求来的时候才创建连接,而是在应用程序一启动(连接池对象一创建)就会创建一定数量的连接

  • 连接池会维护一个最小连接数和最大连接数 ,来保证连接池始终有足够用的可用连接。

    当连接池中的连接数小于最小连接数时,连接池会创建新的连接;

    当连接池中的连接数大于最大连接数时,连接池会关闭一些空闲连接

    同时,连接池还会根据一定的策略来检查连接的可用性,如果连接失效,则会被从连接池中移除。

  • 连接池会维护一个最小空闲数和最大空闲数 。释放连接时,会对连接池中的连接进行判断,如果已经达到最大空闲连接,就会直接将该连接对象丢弃。

  • 连接池中的空闲连接并不会一直存在 ,连接池会根据需要动态地创建和关闭连接,以保证连接池中的连接数始终在最小连接数和最大连接数之间。连接池还会做空闲连接检测,如果该连接空闲的时间超过了其设置的空闲时间,就会被清除。需要获取连接时,连接池会优先返回空闲时间较短的连接 ,以提高连接的可用性和效率。赶时间被淘汰的先调用!增连接的可用性。

  • 一般情况下,当应用程序关闭时,连接池中的所有连接都会被关闭。但是,有些连接池实现可能会留下一些连接处于空闲等待状态,以便下次应用程序启动时可以更快地获取连接。这些连接通常被称为“保持连接”或“保持活动连接”。

4.2、创建连接池

1、创建连接池管理对象,创建连接池对象,然后通过连接池管理对象去获取连接池对象的连接

(这种方式适用于管理有很多歌连接池,比如Mysql连接池、Orcale连接池等等。但是我们一般其实只会有一种连接池,所以没必要写连接池管理对象。在这里只是列出这种创建方式)

  • 创建连接池管理类DBConnManager
package db.test;
import java.sql.*;
import java.util.*;

/*连接池管理类,可以管理多个数据库连接池*/
public class DBConnManager {
	//连接池名列表
	private Vector poolnames = new Vector();
	//驱动程序名列表
	private Vector drivernames = new Vector();
	//数据库标识列表
	private Vector dbids = new Vector();
	//用户名列表
	private Vector usernames = new Vector();
	//密码列表
	private Vector passwds = new Vector();
	//最大连接数列表
	private Vector maxconns = new Vector();
	//连接池队列
	private Hashtable connPools = new Hashtable();
	
	public DBConnManager() {
		//添加mysql数据库的连接信息
		poolnames.addElement("mysql");
		drivernames.addElement("org.gjt.mm.mysql.Driver");
		dbids.addElement("jdbc:mysql://localhost/hibernate_fistb");
		usernames.addElement("root");
		passwds.addElement("mysql");
		maxconns.addElement("5");
		//创建连接池
		createPools();
	}
	
	/*将连接返回给由指定的连接池*/
	public void releaseConnection(String name, Connection con) {
		DBConnPool pool = (DBConnPool) connPools.get(name);
		if (pool != null)
			pool.releaseConnection(con);
	}
	
	/*得到一个指定连接池中的连接*/
	public Connection getConnection(String name) {
		DBConnPool pool = (DBConnPool) connPools.get(name);
		if (pool != null)
			return pool.getConnection();
		return null;
	}
	
	/*关闭所有连接*/
	public synchronized void closeConns() {
		Enumeration allPools = connPools.elements();
		while (allPools.hasMoreElements()) {
			DBConnPool pool = (DBConnPool) allPools.nextElement();
			pool.closeConn();
		}
	}
	
	/*创建连接池*/
	private void createPools() {
		for(int i = 0; i<poolnames.size();i++){
			String poolname = poolnames.elementAt(i).toString();
			String drivername = drivernames.elementAt(i).toString();
			String dbid = dbids.elementAt(i).toString();
			String username = usernames.elementAt(i).toString();
			String passwd = passwds.elementAt(i).toString();
			int maxconn=0;
			try {
				maxconn = Integer.parseInt(maxconns.elementAt(i).toString());
			}
			catch (NumberFormatException e) {
				e.printStackTrace();
			}
			DBConnPool pool = new DBConnPool(poolname, drivername, dbid, username, passwd, maxconn);
			connPools.put(poolname, pool);
		}
	}
}

可以看到连接池管理类中,维护了很多个容器,用来放连接池的参数,从而创建多个连接池并进行管理。并且用到的都是线程安全的容器,如vector、HashTable。

当创建DBConnManager对象时,调用构造方法,创建出连接池createPools()

通过DBConnManager,调用public Connection getConnection(String name)方法,获得指定连接池的连接

通过DBConnManager,调用public void releaseConnection(String name, Connection con)方法,将指定的连接放入到指定的连接池中

当程序运行结束,通过DBConnManager,调用public synchronized void closeConns()方法,关闭所有连接

创建连接池对象

package db.test;
import java.sql.*;
import java.util.*;

/*连接池类.能够根据要求创建新连接,直到最大连接数为止.*/
public class DBConnPool {
	//实际使用中的连接数
	private int inUse=0;
	//空闲连接
	private Vector connections = new Vector();
	//连接池名
	private String poolname;
	//数据库标识
	private String dbid;
	//驱动程序名
	private String drivername;
	//数据库账号
	private String username;
	//数据库密码
	private String passwd;
	//最大连接数
	private int maxconn;

	public DBConnPool(String poolname, String drivername, String dbid, String username, String passwd, int maxconn) {
		this.poolname = poolname;
		this.dbid = dbid;
		this.drivername = drivername;
		this.username = username;
		this.passwd = passwd;
		this.maxconn = maxconn;
	}

	/*将连接返回给连接池*/
	public synchronized void releaseConnection(Connection con) {
		// 将指定连接加入到向量末尾
		connections.addElement(con);
		//连接数减一
		inUse--;
	}

	/*从连接池得到一个连接*/
	public synchronized Connection getConnection() {
		Connection con = null;
		if (connections.size() > 0) {
			// 获取连接列表中获得第一个连接
			con = (Connection) connections.elementAt(0);
			connections.removeElementAt(0);
			//如果此连接已关闭,则继续获取
         try {
            if (con.isClosed())
               con = getConnection();
         }
         catch (Exception ex) {
            ex.printStackTrace();
         }
		}
		//如果实际使用的连接小于最大连接数,就新创建一个连接
		else if (maxconn == 0 || inUse < maxconn) {
			con = newConnection();
		}
		if (con != null) {
			//连接数增一
			inUse++;
		}
		//返回一个连接
		return con;
	}

	/*创建新的连接*/
	private Connection newConnection() {
		Connection con = null;
		try {
			//加载驱动程序
			Class.forName(drivername);
			//建立连接
			con = DriverManager.getConnection(dbid, username, passwd);
		}
		catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		//返回该连接
		return con;
	}

	/*关闭所有连接*/
	public synchronized void closeConn() {
		Enumeration allConnections = connections.elements();
		while (allConnections.hasMoreElements()) {
			Connection con = (Connection) allConnections.nextElement();
			try {
				con.close();
			}
			catch (SQLException e) {
				e.printStackTrace();
			}
		}
		connections.removeAllElements();
	}
}

在jsp中获取连接

<html>
<%@ page contentType="text/html;charset=GB2312" %>
<%@ page import="java.sql.*" %>
<jsp:useBean id="connManager" scope="application" class="db.test.DBConnManager" />
<body bgcolor="#CFF1E1">
<%
    
   Connection con2 = connManager.getConnection("mysql");
   if(con2==null)
   {
%>
对不起,现在数据库忙,请稍后再试
<%
   }
    
   Statement stmt2 = con2.createStatement();
    
%>
<tr>
<td>用户id</td>
<td>用户姓名</td>
<td>用户密码</td>
</tr>
</tr>
<%
   ResultSet rs=stmt2.executeQuery("select * from user");
   while(rs.next())
   {
%>
<tr>
<td><%=rs.getString("id")%></td>
<td><%=rs.getString("name")%></td>
<td><%=rs.getString("password")%></td>
</tr>
<%	
   }
   rs.close();
   stmt2.close();
   connManager.releaseConnection("mysql",con2);
%>
 
</table>
 
<%
    connManager.closeConns();
%>    
</body>
</html>

使用完一定要关闭资源!

   rs.close();//关闭结果集
   stmt2.close();//关闭statemnt对象
   connManager.releaseConnection("mysql",con2);//释放连接
   connManager.closeConns();//程序结束后,关闭连接池

2、优化----单一连接池的创建

package com.cx.bank.config;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;

/*连接池类.能够根据要求创建新连接,直到最大连接数为止.*/
public class DBConnPool {
    //实际使用中的连接数
    private int inUse=0;
    //空闲连接
    private final Vector<Connection> connections = new Vector<>();
    //数据库标识
    private String url;
    //驱动程序名
    private final String drivername;
    //数据库账号
    private final String username;
    //数据库密码
    private final String password;
    //最大连接数
    private final int maxCount;

    public DBConnPool() throws IOException {
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("mysql.properties");
        Properties properties = new Properties();
        properties.load(inputStream);
        inputStream.close();
        this.drivername = properties.getProperty("driver");
        this.username = properties.getProperty("username");
        this.password = properties.getProperty("password");
        this.maxCount = Integer.parseInt(properties.getProperty("maxCount"));
        this.url=properties.getProperty("url");
    }

    /*将连接返回给连接池*/
    public synchronized void releaseConnection(Connection con) {
        // 将指定连接加入到向量末尾
        connections.addElement(con);
        //连接数减一
        inUse--;
    }

    //从连接池得到一个连接
    public synchronized Connection getConnection() {
        Connection con = null;
        if (connections.size() > 0) {
            // 获取连接列表中获得第一个连接
            con = connections.elementAt(0);
            connections.removeElementAt(0);
            //如果此连接已关闭,则继续获取
            try {
                if (con.isClosed()) {
                    con = getConnection();
                }
            }
            catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        //如果实际使用的连接小于最大连接数,就新创建一个连接
        else if (inUse < maxCount) {
            con = newConnection();
        }
        if (con != null) {
            //连接数增一
            inUse++;
        }
        //返回一个连接
        return con;
    }

    /*创建新的连接*/
    private Connection newConnection() {
        Connection con;
        try {
            //加载驱动程序
            Class.forName(drivername);
            //建立连接
            con = DriverManager.getConnection(url, username, password);
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        //返回该连接
        return con;
    }


    //关闭所有连接
    public synchronized void closeConn() {
        Enumeration allConnections = connections.elements();
        while (allConnections.hasMoreElements()) {
            Connection con = (Connection) allConnections.nextElement();
            try {
                con.close();
            }
            catch (SQLException e) {
                e.printStackTrace();
            }
        }
        connections.removeAllElements();
    }
}


将mysql数据库的配置文件放在一个properties文件中,方便要更改配置时不用改源码。

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/banksys
username=root
password=123456
maxCount=5

  • 使用的时候注意,每次从连接池中获得的连接,只在当前请求中有效!并且,请求的连接用完之后一定要记得释放连接,这样才能实现复用。
  • 一般连接池对象都要在初始化就创建好,方便随时调用。

4.3、使用第三方连接池

1、常见第三方连接池:

  • druid
  • dbcp
  • c3p0

2、使用

  • 导入依赖
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.2.4</version>
 </dependency>
  • 编写基本配置
public class JdbcUtils {
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    // 获取连接对象
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    // 释放资源
    public static void release(Connection conn, PreparedStatement ps, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    static {
        try {
            // 设置连接池参数
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
            dataSource.setUser("root");
            dataSource.setPassword("123456");
            dataSource.setMaxPoolSize(20);
            dataSource.setMinPoolSize(5);
            dataSource.setInitialPoolSize(10);
            dataSource.setMaxIdleTime(60);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 测试使用
public class UserDao {

    // 查询用户信息
    public User getUserById(int id) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        User user = null;
        try {
            // 从连接池中获取连接对象
            conn = JdbcUtils.getConnection();
            String sql = "select * from user where id=?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, id);
            rs = ps.executeQuery();
            if (rs.next()) {
                user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setAge(rs.getInt("age"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 将连接对象归还给连接池
            JdbcUtils.release(conn, ps, rs);
        }
        return user;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zero摄氏度

感谢鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值