Java大数据之路--JDBC

  • JDBC

 

目录

JDBC

JDBC引入 

JDBC实现

程序详解

DriverManager:

数据库URL

Connection 

Statement

ResultSet

 释放资源

 JDBC工具类

SQL注入攻击

PreparedStatement概述

批处理机制

连接池

     代码实现

开源数据库连接池

    DBCP(apache开源的一个连接池)

    c3p0开源连接池



  • JDBC引入 

为了能让程序员利用java程序操作数据库,数据库厂商提供了一套jar包,通过导入这个jar 包就可以直接调用其中的方法操作数据库,这个jar包称之为驱动。

mysql-connector-java-5.0.8-bin.jar

行业中有很多种的数据库,要使用这么多数据库需要学习很多数据库驱动,对于程序员来说,学习成本非常高, 想要让java程序兼容数据库,所有的数据库驱动都实现了jdbc这套接口。

  • JDBC实现

  1. 注册数据库驱动
  2. 获取数据库连接
  3. 创建传输器
  4. 传输sql并返回结果
  5. 遍历结果
  6. 关闭资源 
package cn.zyj.jdbc;
import com.mysql.jdbc.Driver;
import java.sql.*;
public class fxDemo1 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
            //注册驱动
            DriverManager.registerDriver(new Driver());
            //获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb5", "root", "");
            //创建传输器
            stat = conn.createStatement();
            //传输sql并返回结果
            rs = stat.executeQuery("select * from exam");
            //遍历结果
            while (rs.next()) {
                int id = rs.getInt(1);
                String name = rs.getString(2);
                System.out.println("id=" + id + "name=" + name);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    rs=null;
                }
            }
            if (stat!=null){
                try {
                    stat.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    stat=null;
                }
            }
            if (rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    rs=null;
                }
            }
        }
    }
}
  • 程序详解

  • DriverManager:

用于加载驱动,并创建与数据库的连接,这个API的常用方法:

DriverManager.registerDriver(new Driver());

DriverManager.getConnection(url,user,password);

注意(在实际开发中并不推荐采用registerDriver方法注册驱动)原因有二:

  1. 查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
  2. 程序依赖mysqlapi,脱离mysqljar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。

 推荐方式:Class.forName("com.mysql.jdbc.Driver");

  1. 采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
  2. 同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection象。
package cn.zyj.jdbc;
import java.sql.*;
public class fxDemo2 {
    public static void main(String[] args) {
        //定义
        Connection conn=null;
        Statement stat=null;
        ResultSet rs= null;
        try {
            //加载注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取数据库连接
            conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb5","root","");
            //创建传输器
            stat=conn.createStatement();
            //传输sql并返回结果
            rs=stat.executeQuery("select * from exam");
            while (rs.next()){
                int id = rs.getInt(1);
                String name = rs.getString(2);
                System.out.println("id="+id+"name="+name);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //这边写了一个工具类进行资源关闭,关闭方法和之前的代码相似
            JDBCutils.close(conn,stat,rs);
        }
    }
}
  • 数据库URL

Oracle写法:jdbc:oracle:thin:@localhost:1521:sid

SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid

MySql—jdbc:mysql://localhost:3306/sid

Mysqlurl地址的简写形式: jdbc:mysql:///sid

常用属性:useUnicode=true&characterEncoding=UTF-8

  • Connection 

Jdbc程序中的Connection,它用于代表数据库的链接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:

createStatement():创建向数据库发送sqlstatement对象。

prepareStatement(sql) :创建向数据库发送预编译sqlPrepareSatement对象。

prepareCall(sql):创建执行存储过程的callableStatement对象。

setAutoCommit(boolean autoCommit):设置事务是否自动提交。

setAutoCommit(boolean autoCommit):设置事务是否自动提交。

commit() :在链接上提交事务。rollback() :在此链接上回滚事务。

  • Statement

Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:

executeQuery(String sql) 用于向数据发送查询语句。

executeUpdate(String sql)用于向数据库发送insertupdatedelete语句

execute(String sql):用于向数据库发送任意sql语句

  • ResultSet

 Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。

ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:

获取指定类型的数据,例如:getString(int index)getString(String columnName)

常用数据类型转换表

API:

ResultSet还提供了对结果集进行滚动的方法:

next():移动到下一行

Previous():移动到前一行

absolute(int row):移动到指定行

beforeFirst():移动resultSet的最前面。

afterLast() :移动到resultSet的最后面。

  •  释放资源

为什么要关闭资源?

在安装数据库的时候,设置过最大连接数量,如果用了不还连接,别人就无法使用了。

rs对象中可能包含很大的一个数据,对象保存在内存中,这样就十分占用内存。需要将他关闭。最晚创建的对象,最先关闭。

Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, StatementConnection对象。

特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。

为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中 

释放资源

在关闭过程中可能会出现异常,为了能够关闭资源,需要将资源在finally中关闭

如果在finally中关闭资源则需要将conn,stat,rs三个对象定义成全局的变量

conn,stat,rs三个变量出现异常的时候可能会关闭不成功,我们需要将他们在finally中置为nullconn,stat,rs这三个对象是引用,将引用置为null,它引用的对象就会被JVM回收,也能保证资源的释放。

 if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    rs=null;
                }
            }
            if (stat!=null){
                try {
                    stat.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    stat=null;
                }
            }
            if (rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    rs=null;
                }
            }
  •  JDBC工具类

package cn.zyj.jdbc;
import java.io.File;
import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;
//工具类(工厂模式)
public class JDBCutils {
    //类中的方法只能通过类名.的方式来调用
    public static  Properties prop= new Properties();
    private JDBCutils(){}
    //创建连接
    public static Connection getConnection() throws Exception {
        //获取类加载器JDBCutils.class.getClassLoader()
        // 通过类加载器获取src目录getResource()
        //直接在括号中书写文件名称即可得到文件路径
        //getPath()是为了将url转换为String类型的数据
        prop.load(new FileInputStream(new File(JDBCutils.class.getClassLoader().getResource("conf.properties").getPath())));
        Class.forName(prop.getProperty("driver"));
        //创建数据库连接
       return DriverManager.getConnection(prop.getProperty("url"),
               prop.getProperty("user"),prop.getProperty("password"));
    }
    //关闭资源
    public  static void close(Connection conn, Statement stat, ResultSet rs){
        //关闭资源,后创建的先关闭
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } finally {
                rs = null;
            }
        }
        if (stat != null) {
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } finally {
                stat = null;
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } finally {
                conn = null;
            }
        }
}
}
//con.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb5
user=root
password=
  • SQL注入攻击

由于sql语句是由前台参数与后台语句拼接而来,在用户传入的参数位置可能会输入数据库的关键字。这些关键字可能是sql语句的语义发生改变,从而达到一些特殊的效果,这种操作方式称之为sql注入攻击。

a. 攻击方式在参数中添加 '# 改变语义。

b. 解决方案:i. 利用Statement子接口PreparedStatement可以有效防止sql注入攻击。 

  • PreparedStatement概述

PreparedStatement是Statement的一个子接口,具有预编译功能

PreparedStatment发送sql的步骤:

1、先将sql主干语句部分发送到数据库服务器中,把参数位置用?来预留,sql语句到达服务器之后会变成一段二进制的机器码,这段机器码不能被操作

2、再将sql语句中的参数发送到数据库服务器中,这些参数以存文本的形式进行发送。也就是加上了单引号‘’

PreparedStatment的优势:

参数可以单独传入,避免sql语句的拼接错误,用有预编译功能防止sql注入攻击

  • 批处理机制

 sql语句执行过程中,每个JDBC六步仅操作一个语句,如果有多个sql要执行,则在成很大的代码冗余,书写不便利。可以将这些sql语句放入一个JDBC的批处理中,一同发送的数据库服务器执行。 

  • statement批处理

stat.addBatch(String sql); 添加sql 到批处理中

stat.addBatch(String sql);

stat.addBatch(String sql);

stat.executeBatch(); 执行批处理

package cn.zyj.batch;

import cn.zyj.jdbc.JDBCutils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class sbdfx1 {
    public static void main(String[] args) {
        //定义
        Connection conn =null;
        Statement stat=null;
        ResultSet rs = null;
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取数据库连接
            conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb5","root","");
            //创建传输器
            stat=conn.createStatement();
            stat.addBatch("create table t2(id int,name varchar(20))");
            stat.addBatch("insert into t2 values(1,'鸣人')");
            stat.addBatch("insert into t2 values(2,'小樱')");
            stat.addBatch("insert into t2 values(3,'做主')");
            stat.addBatch("insert into t2 values(4,'hh')");
            //执行批处理
            stat.executeBatch();
            System.out.println("插入成功");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.close(conn,stat,null);
        }
    }
}

ps = conn.preparedStatement(String sql);

ps.setString(1,5) 

ps.addBatch() 添加sql参数 到批处理中

ps.executeBatch() 执行批处理

ps.clearBatch(); 清空批处理 

  • PreparedStatement批处理
package cn.zyj.batch;
//PreparedStatement批处理
/*
PreparedStatement特点
优点:
    有预编译功能
    将sql主干预留在数据库服务器中,不必重复发送sql语句
    每次仅发送sql参数部分,执行效率较高
缺点:
    只能执行同一语义的sql
**/

import cn.zyj.jdbc.JDBCutils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

public class PBD1 {
    public static void main(String[] args) {
        Connection conn =null;
        PreparedStatement ps = null;
        ResultSet rs=null;
        try {
            conn= JDBCutils.getConnection();
            ps=conn.prepareStatement("insert into t1 values(?,?)");
            for (int i = 0; i < 100000; i++) {
                ps.setInt(1,i);
                ps.setString(2,"name"+i);
                ps.addBatch(); //添加sql参数到批处理中
                if (i%1000==0){
                    ps.executeBatch(); //执行批处理
                    ps.clearBatch();  //清空批处理
                    System.out.println("执行完毕,当前批次数为"+i/1000);
                }
            }
            //循环可能有不慢1000的数据,通过本句来执行
            ps.executeBatch();
            System.out.println("PreparedStatement执行完毕");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.close(conn,ps,rs);
        }
    }
}
  • 连接池

在使用JDBC过程中,连接的使用占用资源较少,而创建连接和销毁连接占用资源较多。为了减少在服务器和数据库服务器之间,创建连接和销毁连接的过程,可以使用连接池代替原本JDBC有关连接的操作 。

原理:连接池在服务器启动的时候,会自动向数据库服务器索要一批连接,这些连接会保留在连接池中,用户需要访问数据库时,可以从连接池中取出连接。使用完成后,可以归还连接,从而省去了创建和销毁的过程。

  •  代码实现

package cn.zyj.pool;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.*;
import java.util.LinkedList;
import java.util.logging.Logger;
public class MyPool implements DataSource {
    //当前类加载时初始化一批连接
    public static LinkedList<Connection> pool = new LinkedList();
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            for (int i = 0; i < 5; i++) {
                Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb5","root","");
                //每次创建对象的地址不同
                pool.add(conn);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public Connection getConnection() throws SQLException {
        //获取连接
        if (pool.size()==0){
            for (int i = 0; i < 5; i++) {
                Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb5","root","");
                //每次创建对象的地址不同
                pool.add(conn);
            }
        }
        Connection conn=pool.remove(0);
        System.out.println("从池中取出连接,池中还剩"+pool.size()+"个连接");
        return conn;
    }
    public void returnConnection(Connection conn) throws SQLException {
        if (conn!=null&&!conn.isClosed()){
        pool.add(conn);
            System.out.println("归还连接,池中还剩"+pool.size()+"个连接");
        }
    }
    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
    }
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }
    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

 


package cn.zyj.pool;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/*测试*/
public class TestPool {
    public static void main(String[] args) {
        Connection conn =null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        MyPool pool = new MyPool();
        try {
            conn=pool.getConnection();
            ps=conn.prepareStatement("select * from exam where id=?");
            ps.setInt(1,1);
            rs=ps.executeQuery();
            while (rs.next()){
               String name= rs.getString("name");
                System.out.println("name"+name);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //关闭资源,后创建的先关闭
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    rs = null;
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    ps = null;
                }
            }
            if (conn != null) {
                try {
                    pool.returnConnection(conn);
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    conn = null;
                }
            }
        }
    }
}
console
从池中取出连接,池中还剩4个连接
name关羽
归还连接,池中还剩5个连接

  • 开源数据库连接池

  • DBCP(apache开源的一个连接池)

package cn.zyj.pool;

import cn.zyj.jdbc.JDBCutils;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

public class DBCPDemo1 {
    public static void main(String[] args) throws Exception {
        Connection conn = null;
        Statement stat=null;
        ResultSet rs= null;
       /* BasicDataSource source = new BasicDataSource();
        source.setDriverClassName("com.mysql.jdbc.Driver");
        source.setUrl("");
        source.setUsername("");
        source.setPassword("");*/
       //利用工厂生产一个dbcp数据源对象
        try {
            Properties prop = new Properties();
            prop.load(new FileInputStream(new File(DBCPDemo1.class.getClassLoader().getResource("dbcp.properties").getPath())));
            BasicDataSourceFactory factory = new BasicDataSourceFactory();
            DataSource source = factory.createDataSource(prop);
            conn = source.getConnection();
            stat = conn.createStatement();
            rs=stat.executeQuery("select * from exam");
            while (rs.next()){
               int id = rs.getInt(1);
               String name = rs.getString(2);
                System.out.println("id="+id+"name="+name);
            }
        }catch (Exception e){
            throw  new RuntimeException();
        }finally {
            //conn.close() dbcp重写 是归还连接
            JDBCutils.close(conn,stat,rs);
        }
    }


}
dbcp.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///mydb5
username=root
password=
  • c3p0开源连接池

package cn.zyj.pool;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/*
C3P0连接池测试使用
* */
public class c3p0Demo1 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat=null;
        ResultSet rs= null;
        ComboPooledDataSource source = new ComboPooledDataSource();
        try {
            /*source.setDriverClass("com.mysql.jdbc.Driver");
            source.setJdbcUrl("jdbc:mysql://localhost:3306/mydb5");
            source.setUser("root");
            source.setPassword("");*/
            conn=source.getConnection();
            stat=conn.createStatement();
            rs=stat.executeQuery("select * from exam");
            while(rs.next()){
                String name = rs.getString(2);
                int id = rs.getInt(1);
                System.out.println("id="+id+"name="+name);

            }
        }  catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //关闭资源,后创建的先关闭
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    rs = null;
                }
            }
            if (stat != null) {
                try {
                    stat.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    stat = null;
                }
            }
            if (conn != null) {
                try {
                    //归还连接
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    conn = null;
                }
            }
        }
    }
}
c3p0-config.xml
<?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/mydb5</property>
        <property name="user">root</property>
        <property name="password"></property>
    </default-config>
</c3p0-config>

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值