JDBC管理数据库

JDBC

1.概念

 1)从物理结构:JDBC是Java语言访问数据库的一套接口(API)集合。
 2)从本质上:JDBC是调用者(开发人员)和实现者(数据库厂商)之间的协议。
 3)JDBC的实现由数据库厂商以驱动程序的形式提供。
 4)开发人员借助JDBC的API,就可以使用纯Java的方式来连接和操纵数据库。 

2.常用类和接口

1)接口
Connection 连接 代表了java和数据之间的通道,桥梁
Statement  语句 可以用来执行 insert, update, delete , select ...
ResultSet  结果集 代表的是查询的结果
2)类
DriverManager 工具类,获取连接对象
SQLException  异常对象 是 Exception的子类型,属于检查异常

3.操作步骤

1) 加载驱动 (Driver) jdbc的驱动就是一个连接工厂,生成的产品是连接对象
   com.mysql.jdbc.Driver 是Driver的mysql实现类
   oracle.jdbc.Driver    是Driver的oracle实现类
   jdbc 3.0 以上版本都可以省略加载驱动这一步
2) 获取连接对象
3) 创建语句
4) 执行sql
5) 释放资源 ( 先打开的资源后关闭 )

4.代码示例

import java.sql.*;

public class TestJdbc {

    public static void main(String[] args) throws SQLException {
        // 1. 获取连接对象
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        System.out.println(conn.isClosed());

        // 2. 准备sql语句
        Statement stat = conn.createStatement();

        // 3. 执行sql语句
//        stat.executeUpdate("insert into dept(deptno,dname,loc) values(60,'市场部','西安')"); // 执行增删改
        ResultSet rs = stat.executeQuery("select * from dept");// 执行查询

        while(rs.next()) {
            // 移动到下一条记录 , 返回true表示有下一条记录, 返回false表示没有下一条了
            System.out.println(rs.getInt("deptno")+" "+rs.getString("dname") +" " +rs.getString("loc"));
        }   
        // 4. 释放资源
        stat.close();
        conn.close();
    }
}

PreparedStatement 预编译语句对象

操作步骤

Statement stmt = conn.createStatement();
stmt.executeUpdate(String sql);

  1. 需要预先提供sql语句
PreparedStatement psmt = conn.prepareStatement(String sql);
  1. 可以在sql中用?占位某个值
insert into student(sid,sname,birthday,sex) values(null, ?, ?, ?)
  1. 给?赋值
使用PreparedStatement中一系列以 set开头的方法
setString(?的位置,)
setInt(?的位置,)
setDate(?的位置,)

psmt.setString(1, "李四");
psmt.setString(2, "1999-9-7");
  1. 执行sql
psmt.executeUpdate();

注意: ?能够占位的只有值, 不能是表名、列名、关键字

代码示例
编写一个方法,以学生的姓名作为条件查询,返回集合
学生类如下

import java.util.Date;
//创建学生类
public class Student {
    private int sid;
    private String sname;
    private Date birthday;
    private String sex;

    public Student(int sid, String sname, Date birthday, String sex) {
        this.sid = sid;
        this.sname = sname;
        this.birthday = birthday;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                '}';
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

查询如下

import java.sql.*;
import java.sql.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class TestJdbc3 {
    public static void test() {
        try (Connection conn = JdbcUtils.getConnection()) {
            String sql = "insert into student(sid,sname,birthday,sex) values(null, ?, ?, ?)";
            // ctrl+p Statement.RETURN_GENERATED_KEYS 是一个选项,表示要返回自增的主键值
            try (PreparedStatement psmt
                         = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS) ) {
                psmt.setString(1, "aaa");
                int rows = psmt.executeUpdate();
                System.out.println("影响行数:" + rows);
                ResultSet rs = psmt.getGeneratedKeys();
                // 就一行一列
                rs.next();
                int id = rs.getInt(1);
                System.out.println("主键值是:" + id);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

public static Student findById(int sid) {
        try(Connection conn = JdbcUtils.getConnection()) {
            try (PreparedStatement psmt = conn.prepareStatement("select * from student where sid = ?")) {
                psmt.setInt(1, sid);
                ResultSet rs = psmt.executeQuery();
                // 查询结果要么1条,要么是0条
                if(rs.next()) { // 找到了
                    String sname = rs.getString("sname");
                    Date birthday = rs.getDate("birthday");
                    String sex = rs.getString("sex");
                    Student student = new Student(sid, sname, birthday, sex);
                    return student;
                } else { // 没找到
                    return null;
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

事务控制

1.操作步骤

begin; // 开始事务 start transaction;
多条sql语句
commit; // 提交事务, 最终确认,让所有修改生效
rollback; // 回滚事务,撤销事务内所有的修改
jdbc 默认是让每条sql的执行作为一个独立的事务

2.自动提交

    //值为true表示sql命令的提交(commit)由驱动程序负责
    Connection.setAutoCommit(true);

3.手动提交

try {
    Connection.setAutoCommit(false);
    // 执行多条sql 接下来,事务会在执行第一条 insert, update, delete 时 开始
    // 提交事务
    Connection.commit();
} catch(Exception e ) {
    // 回滚事务
    Connection.rollback();
}
try(Connection conn = JdbcUtils.getConnection()) {
    try {
        // 让事务变成手动提交
        conn.setAutoCommit(false);
        PreparedStatement stmt = conn.prepareStatement("delete from student where sid = ?");
        stmt.setInt(1, 1005);
        // begin
        stmt.executeUpdate();
        int i = 1 / 0; // 出现异常
        stmt.setInt(1, 1007);
        stmt.executeUpdate();
        conn.commit(); // 手动提交事务
    } catch (SQLException e) {
        conn.rollback(); // 手动回滚事务
    }
} catch (SQLException e) {
    e.printStackTrace();
}

性能提升

1. 批量增删改
批处理
PreparedStatement.addBatch() 加入批处理包
PreparedStatement.executeBatch() 把批处理包中所有sql,一次性发送给数据库服务器
对于mysql需要通过连接参数,开启批处理功能:
在连接字符串之后添加:
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useCursorFetch
其中3306是mysql服务器的端口,rewriteBatchedStatements=true表示批处理,useCursorFetch是为了避免内存溢出
示例代码

try(Connection conn = JdbcUtils.getConnection()) {
    PreparedStatement psmt = conn.prepareStatement("insert into big(name) values(?)");
    long start = System.currentTimeMillis();
    for (int i = 1; i <= 100001; i++) {
        psmt.setString(1, "张" +i);
        psmt.addBatch(); // 10001 10002 .... ... 19999 20000
        if(i % 10000 == 0) {
            psmt.executeBatch(); // 发送给服务器并清空批处理包
        }
    }
    psmt.executeBatch(); // 收尾的
    long end = System.currentTimeMillis();
    System.out.println("共花费了:" +(end-start)+"毫秒");
} catch (SQLException e) {
    e.printStackTrace();
}
  1. 查询性能提升
    返回记录行数 10000
    PreparedStatement.setFetchSize(50);
    mysql 中需要在连接参数中加入: useCursorFetch=true,来开启抓取大小的限制功能
    还可以给fetchSize一个默认值: 连接参数中加入: defaultFetchSize=50

连接池 (connection pool)

javax.sql.DataSource 连接池接口
Conneciton conn = datasource.getConnection(); 从池中返回连接对象
conn.close(); // 一般都覆盖了关闭方法,将连接归还给连接池,而不是真正关闭
用DriverManager 获取连接,称为直连
用DataSource 获取连接,称为池连

代码示例

DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");

//        dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 可选步骤,注册驱动
dataSource.setInitialSize(5); // 初始连接数
dataSource.setMaxActive(10); // 最大连接数
dataSource.setMinIdle(5);    // 最小连接数
dataSource.setValidationQuery("select 1"); // 一条简单的sql语句,用来保活
dataSource.setTestWhileIdle(true); // 当空闲时时不时检查一下连接的有效性, 利用ValidationQuery中的sql语句
dataSource.setTimeBetweenEvictionRunsMillis(60*1000); // 默认一分钟

try(Connection conn = dataSource.getConnection()){

    // conn.close();// 归还连接池了
}

你可能感兴趣的:(JDBC管理数据库)