说是Web基础,其实JDBC并不是专在Web开发中用的
只是因为我现在的主要语言已经从Java转Python了,只有Web开发中会用到JDBC
所以就把这篇扔到Web基础里了
什么是JDBC
全称:Java DataBase Connectivity,顾名思义即Java数据库连接
它是由SUN公司制定的一个与访问数据库有关的API规范,该规范规定了JDBC的使用者(Java、Applet、Servlet等不同类型的程序)如何以统一标准使用JDBC驱动程序与数据库交互。
如何连接MySQL
首先需要一个jar包
MySQL 8.0以下版本
MySQL 8.0 及以上版本
然后把jar包导入到工程中
Eclipse导入外部jar包
一个简单示例
import java.sql.*;
//第一步:导入所需要的包
public class MyJDBC {
static final String JDBC_DRIVER="com.mysql.jdbc.Driver";
//JDBC驱动名,不用改
static final String DB_URL="jdbc:mysql://localhost:3306/YourDatabaseName;
//数据库url
static final String USER="username";
static final String PSWD="pswd";
//你的数据库用户名及密码
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName(JDBC_DRIVER);
//第二步:注册JDBC驱动程序
Connection conn=DriverManager.getConnection(DB_URL,USER,PSWD);
//第三步:建立与数据库的连接
//使用DriverManager的getConnection方法创建一个Connection对象conn
Statement stmt1=conn.createStatement();
Statement stmt2=conn.createStatement();
//第四步:创建Statement对象用于执行SQL语句
/*使用createStatement方法可以创建基本的Statement对象
*此外还可以使用prepareStatent(String sql)创建预编译Statement对象
*使用prepareCall(String sql)创建CallableStatement对象
*/
//一个Connection对象可以创建多个Statement对象
ResultSet rs1=stmt1.executeQuery("SELECT * FROM emailuserpass");
ResultSet rs2=stmt2.executeQuery("SELECT password FROM emailuserpass");
// 第五步:执行SQL语句,使用executeQuery方法时返回一个ResultSet(结果集)对象
/*所有的Statement对象都有三个方法执行SQL语句
* 1.execute:可以执行任何SQL语句,但比较麻烦
* 2.executeUpdate:主要用于执行DML和DDL (INSERT DELETE UPDATE)
* 3.executeQuery:只能查询(SELECT),执行后返回一个ResultSet对象
* */
while(rs1.next()) {
System.out.println("stmt1:"+rs1.getString("emailAddress")+rs1.getString("userName"));
}
while(rs2.next()) {
//第六步:操作结果集(如果有)
//使用next()方法判断是否有下一条记录,初始时指针位于所有记录之前
System.out.println("stmt2:"+rs2.getString("password"));
//使用getType("ColumnName")方法获取本结果集某条记录的某列名对应的值
}
if(rs1!=null) {
rs1.close();
}
if(stmt1!=null) {
stmt1.close();
}
if(rs2!=null) {
rs2.close();
}
if(stmt2!=null) {
stmt2.close();
}
if(conn!=null) {
conn.close();
}
//第七步:关闭资源
}
}
一些说明
- 因为是一个简单示例,所以我写的主方法没有对异常进行处理,直接使用了throws,实际开发中不要这样!
- SQL语言分类(DQL、DML、DDL、DCL)
- 最好使用预编译SQL语句,防止SQL注入
预编译语句的使用
package jdbc_test;
import java.sql.*;
// 第一步:导入包
public class PreparedStatement {
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
//JDBC驱动名,不用改
static final String DB_URL = "";
//数据库url
static final String USER = "";
static final String PSWD = "";
//你的数据库用户名及密码
public static void main(String[] args) {
try {
Class.forName(JDBC_DRIVER);
// 第二步:注册驱动
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String PreparedSQL = "SELECT * FROM emailuserpass WHERE userName = ?";
// 注意:问号就表示SQL语句中的参数,如果是String型不需再加引号!
String UpdateSQL = "UPDATE emailuserpass SET password = ? WHERE userName = ?";
// 一条UPDATE语句
Connection conn = null;
java.sql.PreparedStatement ps = null;
java.sql.PreparedStatement us = null;
ResultSet rs = null;
String parameter = "abc";
String NewPassword = "123";
int EffectedRows = 0;
// 查询用户名为parameter的整条记录,然后将其密码改为123
try {
conn = DriverManager.getConnection(DB_URL, USER, PSWD);
//第三步:获得连接
ps = conn.prepareStatement(PreparedSQL);
us = conn.prepareStatement(UpdateSQL);
//第四步:创建预编译statement对象
ps.setString(1, parameter);
us.setString(1, NewPassword);
us.setString(2, parameter);
//第五步:将预编译statement对象ps的问号处的值替换为不同参数
rs = ps.executeQuery();
//第六步:接收返回的结果集对象
EffectedRows = us.executeUpdate();
if (rs.next()) {
System.out.println(parameter + "处的记录: " + rs.getString("emailAddress") + rs.getString("password"));
//第七步:对结果集进行数据提取
}
else {
System.out.println("username" + parameter + "not found");
}
if (EffectedRows != 0) {
System.out.println(parameter + "密码已更新为" + NewPassword);
}
else {
System.out.println("can not update password");
}
} catch (SQLException se) {
se.printStackTrace();
}
finally {
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (us != null) {
try {
us.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
程序执行前用户名为abc的一条记录:
程序运行结果:
程序运行后查看数据库:
UPDATE操作执行成功
数据库连接池DBCP (DataBase Connection Pool)
JDBC的缺点
上述使用JDBC驱动程序访问数据库虽然简单易用,但存在许多问题
- 每次Web请求都要建立数据库连接,费时费力,对大网站来说是致命缺陷
- 每个数据库连接使用完都要断开,否则可能导致数据库系统内存泄露
- 不能控制被创建的连接数,容易遭受攻击
DBCP的基本思想
- 为数据库连接建立一个缓冲池,在系统初始化时预先创建并向其中放入一定数量的连接,这些连接不能被随意关闭
- 当需要建立数据库连接时,只需在缓冲池中取出一个,使用完毕后放回缓冲池,这样就节省了创建和关闭连接的时间
连接池的三部分 (建立、管理、关闭)
建立:系统初始化时根据相应的配置文件建立连接并放入池中
管理:
客户端请求数据库连接
if (连接池有空闲连接){
将一个空闲连接分配给客户端
此连接标记为正在使用
}
else if (连接数 < 最大连接数maxConn){
创建一个新连接分配给客户端
此连接标记为正在使用
连接数 += 1
}
else{
for(int i = 0, i < maxWaitTime;i++){
if (有占用的连接被释放){
将被释放的连接分配给正在等待的客户端
break
}
else{
等待
}
if (i == maxWaitTime-1){
throw Exceptioin:无空闲连接
}
}
}
客户端使用连接
客户端释放连接
if (该连接引用次数 >= 规定值){
删除该连接
if (当前池内连接数 < 最小连接数minConn){
将连接池充满
}
}
else{
此连接标记为可再分配
}
关闭:应用程序退出时关闭连接池,把向数据库申请的连接对象归还,即关闭所有数据库连接
DBCP配置
可以在tomcat下配置全局的,不过不建议,最好一工程一配
第一步:
根据自己的Java版本下载所需的jar包
commons-dbcp
commons-logging
commons-pool
然后放到 工程名/WebContent/WEB-INF/lib/
下
第二步:
在 工程名/WebContent/META-INF/
下新建context.xml
一定要注意不要写成content.xml…
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<Context>
<Resource
name="jdbc/myDataSource"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
maxActive="100"
maxIdle="30"
maxWait="10000"
username=""
password=""
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/..."
/>
</Context>
参数解析
name:JNDI资源名称,此处表示数据源名称
auth:连接池管理权属性,一般为容器Container
type:对象类型,此处声明为数据库连接池类型
factory:
maxActive:最大活动连接数,为0表示无限制
maxIdle:最大空闲连接数,为0表示无限制
maxWwait:最大等待时长,单位为ms
url:数据库路径
username:连接到数据库url的用户名
password:连接到数据库url的密码
driverClassName:数据库驱动名
第三步:
写一个用于创建、管理、关闭DBCP的类ConnectionPool.java
package dbcp;
import java.sql.*;
import javax.sql.DataSource;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class ConnectionPool {
private static ConnectionPool pool = null;
private static DataSource dataSource = null;
private ConnectionPool() {
try {
InitialContext ic = new InitialContext();
dataSource = (DataSource) ic.lookup ( "java:/comp/env/jdbc/myDataSource");
} catch (NamingException e) {
System.out.println(e);
}
}
public static synchronized ConnectionPool getinstance() {
if (pool == null) {
pool = new ConnectionPool();
}
return pool;
}
public Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e){
System.out.println (e);
return null;
}
}
public void freeConnection(Connection c) {
try {
c.close();
} catch (SQLException e) {
System.out.println(e);
}
}
}
第四步:
使用DBCP,以DBCPTest.jsp
为例
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import = "javax.sql.DataSource" %>
<%@ page import = "javax.naming.*" %>
<%@ page import = "java.sql.*" %>
<%@ page import = "dbcp.ConnectionPool" %>
<!-- 导入写数据库连接池的类-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数据库连接池测试</title>
</head>
<body>
<%
ConnectionPool pool = ConnectionPool.getinstance();
// 获得所有的客户端共用的一个连接池
Connection conn = pool.getConnection();
// 获得一个连接
// 下面和使用JDBC基本类似
ResultSet rs = null;
java.sql.PreparedStatement ps = null;
ps = conn.prepareStatement("SELECT * FROM emailuserpass WHERE userName = ?");
ps.setString(1,"abc");
rs = ps.executeQuery();
if (rs.next()){
out.println(rs.getString("emailAddress"));
}
else {
out.println("Null");
}
pool.freeConnection(conn);
// 直接释放连接即可
%>
</body>
</html>
保存,Run on server,访问此jsp测试一下,成功
DBCP使用总结: