# JDBC入门
[TOC]
## 导学
JDBC的全称为:Java DataBase Connectivity(java数据库连接),这是一门Java连接数据库的技术,可对多种数据库进行统一操作,不需要对单个数据库的驱动进行详细了解。
![](https://img.kancloud.cn/a8/b3/a8b30e66ee4ad65324c040c09eab31e7_817x354.png)
【仅需要了解 JDBC 的规范即可】 - 具体驱动会具体实现相应功能
## 开发步骤
1. 选择数据库:加载数据库驱动
2. 连接数据库
3. 创建数据库查询
4. 获取查询结果
5. 关闭查询和连接
在使用 JDBC 的时候,需要关注的几个问题
* 查询分为操作类(增加、删除、修改)和查询类。
* 要避免重复的创建连接,增加数据库的连接数。
* 注意异常的处理逻辑,保证没有未关闭的无效连接存在。
示例:
~~~
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Demo {
public static void main(String[] args) {
getDataFromDataBase();
}
public static void getDataFromDataBase() {
Connection conn = null;//Connection是与特定数据库连接回话的接口
PreparedStatement ps = null;
try {
//1.注册驱动(静态方法)(包名+类名),告知JVM使用的是哪一个数据库的驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//设置连接地址,时间格式和编码格式(因为版本升级,所以需要加上时间格式)
String url = "jdbc:mysql://localhost:3306/j121study?serverTimezone=UTC&characterEncoding=UTF-8";
String user = "root";
String password = "123456";
//2.连接数据库
conn=DriverManager.getConnection(url,user,password);
/**
* DriverManager类用来管理数据库中的所有驱动程序。
* 是JDBC的管理层,作用于用户和驱动程序之间,跟踪可用的驱动程序,并在数据库的驱动程序之间建立连接。
* 此外,DriverManager类中的方法都是静态方法,所以在程序中无须对它进行实例化,直接通过类名就可以调用。
* DriverManager类的常用方法有getConnection(String url,String user,String password)方法
*/
//3.获取语句执行平台,并创建数据库查询
//Statement s = conn.createStatement();Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。然而查询略显复杂。
//与 Statement一样,PreparedStatement也是用来执行sql语句的,与创建Statement不同的是,需要根据sql语句创建PreparedStatement。
//除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。
ps = conn.prepareStatement("select * from school where school_name=?");
//该语句为参数保留一个问号作为占位符
ps.setString(1, "渡课教育跃龙路主校区");
//4.执行查询语句,并获取查询结果
ResultSet rs = ps.executeQuery();
while(rs.next()) {
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("address"));
}//结果集同样可以释放,以便于下次更方便的填充数据
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(null != conn) {
try {
conn.close();conn = null;
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null != ps) {
try {
ps.close(); ps = null;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
~~~
PrepareStatement中方法
~~~
ResultSet executeQuery(String sql) 执行sql中select语句
int executeUpdate(String sql) 执行sql中的update,insert,delete语句,返回影响条数
~~~
把资源关闭放到finally语句中,最后要把每个关闭了的资源对象设置一个null值。因为这些资源是很宝贵的就算被关闭了也不会马上被回收,设置为null可以让垃圾回收机制更早的回收
## JDBC工具类的抽取
在使用JDBC时,我们发现会有很多代码是重复的,那么其实我们可以将这些代码抽取到工具类中。而且也可以和属性文件搭配使用!避免修改源代码。
jdbc.properties:
~~~
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8
username=root
password=abc
~~~
JDBC工具类:
~~~
public class JDBCUtils {
private static final String driverClass;
private static final String url;
private static final String username;
private static final String password;
static{
// 加载属性文件解析对象:
Properties props = new Properties();
// 如何获得属性文件的输入流?
// 通常情况下使用类的加载器的方式进行获取:类加载器加载JDBCUtils类,并去使用文件资源作为输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
props.load(is);
} catch (IOException e) {
e.printStackTrace();
}
driverClass = props.getProperty("driverClass");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
}
/**
* 注册驱动的方法
* @throws ClassNotFoundException
*/
public static void loadDriver() throws ClassNotFoundException{
Class.forName(driverClass);
}
/**
* 获得连接的方法:
* @throws SQLException
*/
public static Connection getConnection() throws Exception{
loadDriver();
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
}
/**
* 资源释放
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn){
if(rs!= null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
~~~
## SQL注入的漏洞
注入漏洞产生的原因:
**在输入的时候输入了sql语句的关键字**
~~~
public class JDBCDemo4 {
/**
* 测试SQL注入漏洞的方法
*/
public void demo1(){
boolean flag = JDBCDemo4.login2("aaa' or '1=1", "1fsdsdfsdf");
if(flag == true){
System.out.println("登录成功!");
}else{
System.out.println("登录失败!");
}
}
/**
* 避免SQL注入漏洞的方法
*/
public static boolean login2(String username,String password){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
boolean flag = false;
try{
// 获得连接:
conn = JDBCUtils.getConnection();
// 编写SQL:
String sql = "select * from user where username = ? and password = ?";
// 预处理SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数:
pstmt.setString(1, username);
pstmt.setString(2, password);
// 执行SQL:
rs = pstmt.executeQuery();
// 判断结果街
if(rs.next()){
flag = true;
}else{
flag = false;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
return flag;
}
/**
* 产生SQL注入漏洞的方法
* @param username
* @param password
* @return
*/
public static boolean login(String username,String password){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
boolean flag = false;
try{
conn = JDBCUtils.getConnection();
// 创建执行SQL语句的对象:
stmt = conn.createStatement();
// 编写SQL:
String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
// 执行SQL:
rs = stmt.executeQuery(sql);
// 判断结果集中是否有数据。
if(rs.next()){
flag = true;
}else{
flag = false;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
return flag;
}
}
~~~
## 数据库连接池
连接池:
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
在实际应用开发中,特别是在WEB应用系统中,如果JSP、Servlet或EJB使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等步骤,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁发生这种数据库操作,系统的性能必然会急剧下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器中,基本都提供了这项技术,无需自己编程,但是,深入了解这项技术是非常必要的。
数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已经建立的数据库连接,可以克服上述缺点,极大地节省系统资源和时间。
![](https://img.kancloud.cn/fe/b5/feb55c51ba0be3bbd68f7a359d23a4ec_924x463.png)
![](https://img.kancloud.cn/ad/11/ad113c3fa0543d5ef28648bc1fd6722b_934x458.png)
用数据库连接池时,不要再临时创建到数据库的连接,而是直接去使用某个容器或者地址中创建好的connection
>[info]常用的有 : DPCP、C3P0
C3P0 jar包下载地址:[https://sourceforge.net/projects/c3p0/?source=navbar](https://sourceforge.net/projects/c3p0/?source=navbar)
示例:
c3p0配置文件-放在src目录下面
~~~
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
~~~
~~~
public class JDBCUtils2 {
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
/**
* 获得连接的方法:
* @throws SQLException
*/
public static Connection getConnection() throws Exception{
Connection conn = dataSource.getConnection();
return conn;
}
/**
* 资源释放
*/
public static void release(Statement stmt,Connection conn){
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn){
if(rs!= null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
~~~
~~~
public class DataSourceDemo1 {
/**
* 使用配置文件的方式
*/
public void demo2(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
/*// 获得连接:
ComboPooledDataSource dataSource = new ComboPooledDataSource();*/
// 获得连接:
// conn = dataSource.getConnection();
conn = JDBCUtils2.getConnection();
// 编写Sql:
String sql = "select * from user";
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数
// 执行SQL:
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils2.release(rs, pstmt, conn);
}
}
@Test
/**
* 手动设置连接池
*/
public void demo1(){
// 获得连接:
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
// 创建连接池:
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 设置连接池的参数:
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8");
dataSource.setUser("root");
dataSource.setPassword("abc");
dataSource.setMaxPoolSize(20);//设置最大连接数
dataSource.setInitialPoolSize(3);//设置初始化时连接池中有多少连接
// 获得连接:从连接池获得连接
conn = dataSource.getConnection();
// 编写Sql:
String sql = "select * from user";
// 预编译SQL:
pstmt = conn.prepareStatement(sql);
// 设置参数
// 执行SQL:
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, pstmt, conn);
}
}
}
~~~