为什么链接只有java 程序重启才能释放 服务端口间歇通畅 java为什么要连接数据库_Java


Java与数据库连接篇

课程内容:


为什么链接只有java 程序重启才能释放 服务端口间歇通畅 java为什么要连接数据库_sql_02


一、什么是JDBC?(What)

JDBC(Java Data Base Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。

Java对外指定一些编程标准,其它数据库服务器厂商按照这个标准,编写对应的实现方案。这样对于Java程序员而言,只需要学习Java的标准即可,不需要了解不同厂商的实现细节。一切操作都是在学习接口开发。按照接口指定的标准完成指定需要完成的业务即可。

二、为什么要使用JDBC?(Why)

  • 为了将数据持久的存储、固化到磁盘上我们需要掌握数据库。
  • 为了通过Java代码完成对于数据的持久存储我们需要学习JDBC。
  • 通过JDBC我们能够以面向接口方式,操作数据库中的数据,更加的方便。
  • 本身Java就是跨平台的,通过Java开发的JDBC应用可以在不同的平台上执行。
  • JDBC为数据库开发提供了标准的API,所以使用JDBC开发的数据库应用也可以跨数据库(要求全部使用标准的SQL)

JDBC和ODBC的区别和联系:

ODBC:


它使用 C 语言接口,是一组对数据库访问的标准API,这些API通过SQL来完成大部分任务,而且它本身也支持SQL语言,支持用户发来的SQL。ODBC定义了访问数据库API的一组规范,这些API独立于形色各异的DBMS和编程语言。


JDBC:


是Java与数据库的接口规范,JDBC定义了一个支持标准SQL功能的通用低层API,它由Java 语言编写的类和接口组成,旨在让各数据库开发商为Java程序员提供标准的数据库API。


常见JDBC驱动:

  • JDBC-ODBC桥(JDBC-ODBC Bridge Driver)
  • 最早实现的JDBC驱动程序,旨在快速推广,将JDBC-API映射为ODBC-API。JDK8中已经移除
  • JDBC API驱动程序(Anative API partly Java technology-enabled Driver)
  • 需要实现本地安装特定的驱动程序,通过转换把Java编写的JDBC-API转换为Native-API,进行数据库操作。
  • 纯Java的数据库中间件驱动程序(Pure Java Driver for Database Middleware)
  • 不需要安装特定的驱动程序,需要在安装数据库管理系统的服务器端安装中间件(Middleware),中间件负责在存取数据时必要的转换操作。就是将JDBC访问转换为数据库无关的http或者是https协议,再有中间件转换为数据库专用的访问指令。
  • 纯Java的JDBC驱动程序(Direct-to-DatabasePureJavaDriver)
  • 目前最流行的JDBC驱动,不需要安装任何附加内容,所有存取数据库的操作都直接由JDBC驱动程序来完成,此类驱动程序能将JDBC调用转换成DBMS专用的网络协议,能够自动识别网络协议下的特殊数据库并能直接创建数据连接。

三、 在那些场景下需要使用JDBC(Where)

Java程序员如果想要和数据库打交道就必须要掌握JDBC。
如果你的业务模型中需要将数据持久化存储就必须掌握JDBC。

四、 如何使用JDBC?(How)

1. JDBC所处的处境?


为什么链接只有java 程序重启才能释放 服务端口间歇通畅 java为什么要连接数据库_API_03


2.编写第一个JDBC程序

1) 连接步骤


1) 手动导入需要连接数据库的jar包
2) 加载驱动
3) 创建连接对象
4) 声明sql语句
5) 创建处理sql语句对象
6) 发送sql语句,接收返回结果
7) 处理结果
8) 关闭资源连接


2) 导入需要的jar包

连接oracle就导入jdbc的jar包,参照附录到安装对应数据库可以在内部找到jar包;
连接mysql就导入mysql-connector的jar包,到官网下载对应版本jar包,或在maven仓库下载jar包。

3) 编写测试代码


@Test
public void searchOne() {
    //提升作用域 方便后期关闭资源
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try {
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2:获取连接对象
        conn = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/note", 
            "root","root");
        //3:声明sql语句
        String sql = "SELECT * FROM TB_USER WHERE ID = 1";
        //4:创建处理对象
        stmt = conn.createStatement();
        //5:发送sql语句获取结果集(如果是查询操作才存在结果集)
        rs = stmt.executeQuery(sql);
        //6. 迭代结果集
        while(rs.next()) {
            //7:分析结果集
            System.out.println(rs.getString("name"));
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        //8:关闭资源
        try {
            if(rs!=null) 
                rs.close();
            if(stmt!=null) 
                stmt.close();
            if(conn!=null) 
                conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


4) 分析第一个程序

A. 为什么要加载驱动?

I. 通过Class.forName("Driver类的路径名");

II.查看Mysql的Driver类


public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //在初始化的时候,就将当前数据库对象的驱动加载到sql的注册中心中去
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}


B.获取连接源码


//获取连接就是会将连接信息包装为Properties
public static Connection getConnection(String url,
                                       String user, String password) throws SQLException {
    java.util.Properties info = new java.util.Properties();

    if (user != null) {
        info.put("user", user);
    }
    if (password != null) {
        info.put("password", password);
    }

    return (getConnection(url, info, Reflection.getCallerClass()));
}


C.常见对象:

对象名称获取方式用途DriverManager用来管理JDBC驱动的服务类,主要用来获取Connection。ConnectionDriverManager.getConnection(url,name,pwd);代表一个物理连接会话,如果要操作数据库必须先获取数据库连接对象。StatementConnection.createStatement();用于执SQL语句的对象。常见的有Statement、PreparedStatement、CallableStatementResultSet(针对查询)Statement.executQuery(sql);结果集对象,包含了对于访问查询结果的方法。

3. 修改操作

3.1测试用例编写


@Test
public void updateNameById() {
    //1:声明用户修改的id
    Integer id = 1;
    //2:声明修改后的名称
    String newName= "大锤";
    //3:声明连接对象
    Connection conn = null;
    Statement stmt = null;
    try {
        //4:加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //5:获取连接对象
        conn = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/note", 
            "root","root");
        stmt = conn.createStatement();
        //6:声明sql语句
        String sql = "UPDATE TB_USER SET NAME = '"+newName+"'WHERE USERID = "+id;
        //7:发送sql语句 接受返回结果
        int rows = stmt.executeUpdate(sql);
        System.out.println("修改了"+rows+"行记录");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        //8:关闭资源
        try {
            if(stmt!=null) {
                stmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


3.2 注意事项


1. 修改操作返回的是受影响的行数
2. 加载驱动只需要一次,不需要每次都加载驱动


4. 删除操作

4.1 测试用例编写


public class JDBCTest01 {
    Connection conn = null;
    Statement stmt = null;
    @Before
    public void load() {
        //1:加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void deleteById() {
        try {
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/note", 
                    "root","root");
            stmt = conn.createStatement();
            //6:声明sql语句
            String sql =  "DELETE FROM TB_USER WHERE ID = 1";
            //7:发送sql语句 接受返回结果
            int rows = stmt.executeUpdate(sql);
            System.out.println("删除了"+rows+"行记录");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    
    @After
    public void close() {
        try {
            if(stmt!=null) {
                stmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


4.2 注意事项


* 1:删除操作作为DML语句的一种,返回的也是受影响的行数。


5. 增加操作

5.1 测试用例编写


public class JDBCTest01 {
    Connection conn = null;
    Statement stmt = null;
    @Before
    public void load() {
        //1:加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void insert() {
        try {
            User u = new User("wangdachui", "123456",'王大锤', 18, "男");
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/note", 
                    "root","root");
            stmt = conn.createStatement();
            //6:声明sql语句
            String sql =  "INSERT INTO TB_USER (UNAME,UPWD,NAME,AGE,GENDER) VALUES ('"+u.getUname()+"','"+u.getUpwd()+"','"+u.getName()+"','"+u.getAge() + "','" + u.getGender() + "')";
            //7:发送sql语句 接受返回结果
            int rows = stmt.executeUpdate(sql);
            System.out.println("增加了"+rows+"行记录");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @After
    public void close() {
        try {
            if(stmt!=null) {
                stmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


User.java内容如下:


package com.mage.po;
/*
 * javabean规范:
 *  所有属性私有
 * 所有私有属性提供public的setter、getter方法
 * 提供至少空构造器
 */
public class User {
    private int id;
    private String uname;
    private String upwd;
    private String name;
    private String gender;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUname() {
        return uname;
    }
    public void setUname(String uname) {
        this.uname = uname;
    }
    public String getUpwd() {
        return upwd;
    }
    public void setUpwd(String upwd) {
        this.upwd = upwd;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    public User(int id, String uname, String upwd, String name, String gender, int age) {
        super();
        this.id = id;
        this.uname = uname;
        this.upwd = upwd;
        this.name = name;
        this.gender = gender;
        this.age = age;
    }   
}


5.2 注意事项


删除也是返回受影响的行数


注意:对于所有的写操作(增、删、改),其实主体内容都差不多,主要是sql语句不同,某些参数不同,后期是可以考虑封装起来的。

6. 查询操作

6.1 测试用例编写


public class JDBCTest01 {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    @Before
    public void load() {
        //1:加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void insert() {
        LinkedList<User> list = new LinkedList<User>();
        try {
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/note", 
                    "root","root");
            stmt = conn.createStatement();
            //6:声明sql语句
            String sql =  "SELECT * FROM TB_USER";
            //7:发送sql语句 接受返回结果
            rs = stmt.executeQuery(sql);
            //8:分析结果集
            while(rs.next()) {
                User u = new User();
                u.setId(rs.getInt("id"));
                u.setUname(rs.getString("uname"));
                u.setUpwd(rs.getString("upwd"));
                u.setName(rs.getString("name"));
                u.setAge(rs.getInt("age"));
                u.setGender(rs.getString("gender"));
                list.add(s);
            }
            
            System.out.println(ls);
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @After
    public void close() {
        try {
            if(rs!=null) {
                rs.close();
            }
            if(stmt!=null) {
                stmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


6.2 注意事项


1:对于DQL操作,返回的是结果集ResultSet对象
2:对于ResultSet对象,需要通过.next()方法,查看是否存在其他记录


五、附录

1. Statement对象的问题

1.1 效率问题

  • 正常情况下插入:

问题描述:插入100000条数据,通过Statement插入:root 插入数据:1000,用时:2200
插入数据:1000,用时:2769
插入数据:1000,用时:2943


public class JDBCTest02 {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    List<User> data = new LinkedList<>();
    
    @Before
    public void load() {
        try {
            //1:加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2:编写测试数据
            for(int i = 1;i<100000;i++) {
                String uname = "zs"+i;
                String upwd = "123";
                String name = "张三"+i;
                int age = (int)(Math.random()*7+18);
                String gender = (int)(Math.random())>0.5?"女":"男";
                User u = new User(uname, upwd,name, age, gender);
                data.add(u);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void insert() {
        try {
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/note", 
                    "root","root");
            stmt = conn.createStatement();
            //记录时间
            long startTime = System.currentTimeMillis();
            int rows = 0;
            for(int i = 1;i<data.size();i++) {
                //6:声明sql语句
                String sql =  "INSERT INTO TB_User (UNAME,UPWD,NAME,AGE,GENDER) VALUES ('"+
                        data.get(i).getUname()+"','"+data.get(i).getUpwd()+"','"+                           data.get(i).getName()+"','"+ data.get(i).getAge() +"','"+                           data.get(i).getGender()+"')";
                //7:发送sql语句 接受返回结果
                rows += stmt.executeUpdate(sql);
            }
            //记录时间
            long endTime = System.currentTimeMillis();
            System.out.println(endTime-startTime+"ms,总共受影响的行数是:"+rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @After
    public void close() {
        try {
            if(stmt!=null) {
                stmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


结论:
399050ms,总共受影响的行数是:100000

  • 通过PreparedStatement插入数据

通过PreparedStatement插入数据

测试用例:


public class JDBCTest03 {
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    List<User> data = new LinkedList<User>();
    @Before
    public void load() {
        try {
            //1:加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2:编写测试数据
            for(int i = 1;i<100000;i++) {
                String uname = "zs"+i;
                String pwd = "123";
                String name = "张三"+i;
                int age = (int)(Math.random()*7+18);
                String gender = (int)(Math.random())==0?"女":"男";
                User u = new User(uname, pwd,name, age, gender);
                data.add(u);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void insert() {
        int rows = 0;
        try {
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/note", 
                    "root","root");
            //6:声明sql语句
            String sql = "INSERT INTO TB_USER(UNAME,UPWD,NAME,AGE,GENDER) VALUES (?,?,?,?,?)";
            //7:获取预处理对象
            pstmt = conn.prepareStatement(sql);
            //记录时间
            long startTime = System.currentTimeMillis();
            //循环获取到每个对象的内容
            for(int i = 0;i<data.size();i++) {
                //8:设置绑定变量
                pstmt.setString(1,data.get(i).getUname());
                pstmt.setString(2,data.get(i).getUpwd());
                pstmt.setString(3,data.get(i).getName());
                pstmt.setInt(4,data.get(i).getAge());
                pstmt.setString(5,data.get(i).getGender());
                //9:发送SQL语句 获取结果
                rows += pstmt.executeUpdate();
            }           
            //记录时间
            long endTime = System.currentTimeMillis();
            
            System.out.println(endTime-startTime+"ms,总共受影响的行数是:"+rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @After
    public void close() {
        try {
            if(pstmt!=null) {
                pstmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


结论:
677875ms,总共受影响的行数是:100000

  • 通过PreparedStatement批处理以及事务插入数据

通过PreparedStatement批处理插入数据

测试用例:


public class JDBCTest03 {
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    List<Student> data = new LinkedList<>();
    @Before
    public void load() {
        try {
            //1:加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2:编写测试数据
            for(int i = 1;i<100000;i++) {
                String name = "用户"+i;
                String pwd = "123";
                int age = (int)(Math.random()*7+18);
                String gender = (int)(Math.random())>0.5?"女":"男";
                Student stu = new Student(name, pwd, age, gender);
                data.add(stu);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void insert() {
        int rows = 0;
        try {
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/note?rewriteBatchedStatements=true", 
                    "root","root");
            //6:声明sql语句
            String sql = "INSERT INTO TB_USER(UNAME,UPWD,NAMW,AGE,GENDER) VALUES (?,?,?,?,?)";
            //7:获取预处理对象
            pstmt = conn.prepareStatement(sql);
            //记录时间
            long startTime = System.currentTimeMillis();
            //关闭自动提交事务
            conn.setAutoCommit(false);
            //循环获取到每个对象的内容
            for(int i = 0;i<data.size();i++) {
                //8:设置绑定变量
                pstmt.setString(1,data.get(i).getUname());
                pstmt.setString(2,data.get(i).getUpwd());
                pstmt.setString(3,data.get(i).getName());
                pstmt.setInt(4,data.get(i).getAge());
                pstmt.setString(5,data.get(i).getGender());
                //添加到批处理中
                pstmt.addBatch();
            }
            //一起提交到数据库服务器
            rows = pstmt.executeBatch().length;
            //提交事务
            conn.commit();
            //记录时间
            long endTime = System.currentTimeMillis();
            
            System.out.println(endTime-startTime+"ms,总共受影响的行数是:"+rows);
        } catch (SQLException e) {
            try {
                //出现异常则回滚事务
                conn.rollback();;
                e.printStackTrace();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
    }
    @After
    public void close() {
        try {
            if(pstmt!=null) {
                pstmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


结论:
188114ms,总共受影响的行数是:100000

  • Tips
  • 使用批处理需要增加rewriteBatchedStatements=true
  • 将所有批量操作添加到一个事务当中,设置自动提交为false;conn.setAutoCommite(false);
  • 如果语句中出现问题,则在异常中对于事务进行回滚。

1.2 SQL注入问题

测试代码:


public class JDBCTest04 {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    public static final String URL = "jdbc:mysql://localhost:3306/note";
    public static final String USER_NAME = "root";
    public static final String USER_PWD = "root";
    //模拟登陆用户
    User user = null;
    
    @Before
    public void load() {
        try {
            //1:加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2:模拟用户数据,这里记得在User.java中添加对应构造器
            user = new User("'1","'  or 1=1");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void queryByUser() {
        int rows = 0;
        try {
            //5:获取连接对象
            conn = DriverManager.getConnection(
                    URL, USER_NAME,USER_PWD);
            //6:声明sql语句
            String sql = "SELECT * FROM TB_STUDENT WHERE UNAME = "+loginStu.getUname()
                        +" AND PWD = "+loginStu.getUpwd();
            System.out.println(sql);
            //7:获取处理对象
            stmt = conn.createStatement();
            //8:发送sql 获取结果
            rs = stmt.executeQuery(sql);
            //9:分析结果
            while(rs.next()) {
                System.out.println("查询到了结果");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    @After
    public void close() {
        try {
            if(stmt!=null) {
                stmt.close();
            }
            if(conn!=null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


结论:

  • 通过构建sql语句,导致不同的用户名和密码侵入到系统内部,就是SQL注入攻击。
  • 为了防止出现这个问题,我们通过预处理对象完成,PreparedStatement。

2. 常见方法总结

常见对象对象常见方法作用ConnectionConnection.createStatement();创建Statement对象Connection.preparedStatement(sql);创建预处理对象Connection.prepareCall(sql)创建CallableStatement对象,调用存储过程Connection.setAutoCommite(true/false)设置事务提交方式,默认情况是自动提交trueConnection.commit();提交事务Connection.rollback();回滚事务StatementStatement.executeQuery(sql);针对查询操作,获取结果集对象Statement.executeUpdate(sql);针对写(增加、删除、修改)操作,获取受影响的行数PreparedStatementPreparedStatement.executeQuery();针对查询操作,获取结果集对象PreparedStatement.executeUpdate();针对写(增加、删除、修改)操作,获取受影响的行数PreparedStatement.addBatch();添加到批处理中PreparedStatement.executeBatch();执行批处理操作PreparedStatement.setXXX(index,value);设置绑定变量,给指定的占位符index,指定具体的值value,XXX代表value的数据类型。注意index从1开始ResultSetResultSet.next();查看读操作返回的结果集中是否还存在数据信息ResultSet.getXXX(index/columnName);根据字段的顺序或者是字段名称获取结果集中该字段的值,XXX代表返回数据的类型。index获取列是从0开始

3. Statement和PreparedStatement对比

区别点对象具体细节创建方式StatementConnection.createStatement(); 不需要sqlPreparedStatementConnection.preparedStatement(sql);需要sql安全Statement不能防止sql注入<br />SELECT * FROM TB_USER WHERE UNAME = '1 AND PWD = ' OR 1=1 PreparedStatement可以防止SQL注入效率Statement不会初始化,没有预处理,每次都是从0开始执行SQPreparedStatement会先初始化SQL,先把这个SQL提交到数据库中进行预处理,多次使用可提高效率可读性Statement当多次执行参数不同的语句时,需要不断的拼接字符串,使得阅读变得极为耗时以及费力。PreparedStatement通过SetXXX();这样的方式,提高了阅读性性能Statement没有预编译的过程,每次都需要在DB中重新编译一下PreparedStatement语句被db的编译器编译后的执行代码就会被缓存下来, 那么下次调用时只要相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行就可以。 相当于一个函数 , 对于整个db来说,只有预编译的语句和缓存中匹配,那么任何时候,就可以不需要再次编译从而直接执行。并不是所有的预编译语句都会被缓存起来,数据库本身会用一种策略,频繁使用的语句可能被缓存起来,以保存有更多的空间存储新的预编译语句。

3. DBUtiles编写

1) 编写步骤注释


1在src下创建一个.properties文件,编写驱动、链接地址、用户名、密码
2在工具类中通过流的方式读取配置文件信息,获取驱动、链接地址、用户名、密码
3编写获取连接、处理对象的方法
4编写资源连接的方法


2) 具体编码

配置文件DBConfig.properties


database=mysql
#######mysql properties########
mysqlDriver=com.mysql.jdbc.Driver
mysqlUrl=jdbc:mysql://localhost:3306/note
mysqlUsername=root
mysqlPassword=root
#######oracle properties########
oracleDriver=oracle.jdbc.OracleDriver
oracleUrl=jdbc:oracle:thin:@localhost:1521:orcl
oracleUsername=scott
oraclePassword=tiger


编写DBBase接口


public interface DBBase {
    public static final String DATABASE_NAME = "database";
    public static final String DATABASE_DRIVER = "driver";
    public static final String DATABASE_URL = "url";
    public static final String DATABASE_USERNAME = "username";
    public static final String DATABASE_PASSWORD = "password";
}


编写DBUtils工具类


public class DBUtils {
    //声明获取值
    private static String database = "";
    private static String driver = "";
    private static String url = "";
    private static String uname = "";
    private static String pwd = "";
    private static Properties pro = null;
    //通过静态代码块将配置文件信息加载进来
    static {
        try {
            //创建配置对象
            pro = new Properties();
            //获取输入流读取
            InputStream is = Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream("DBConfig.properties");
            //读取数据信息
            pro.load(is);
            //通过方法获取配置文件中的数据信息
            database = getValue(DBBase.DATABASE_NAME);
            driver = getValue(database+"-"+DBBase.DATABASE_DRIVER);
            url = getValue(database+"-"+DBBase.DATABASE_URL);
            uname = getValue(database+"-"+DBBase.DATABASE_USERNAME);
            pwd = getValue(database+"-"+DBBase.DATABASE_PASSWORD);
            //加载驱动
            Class.forName(driver);
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("驱动类为【"+driver+"】");
        }
    }
    /**
     * 获取连接对象
     * @return
     */
    public static Connection getConn() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, uname,pwd);
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("【"+url+"】【"+uname+"】【"+pwd+"】");
        }
        return conn;
    }
    /**
     * 创建Statement对象
     * @param conn
     * @return
     */
    public static Statement getState(Connection conn) {
        if(conn==null)
            throw new RuntimeException("连接为空");
        Statement stmt = null;
        try {
            stmt = conn.createStatement();
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("创建Statement对象失败");
        }
        return stmt;
    }
    //关闭连接
    private static void closeConn(Connection conn) {
        if(conn!=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //关闭资源
    private static void closeStmt(Statement stmt) {
        if(stmt!=null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //关闭资源
    private static void closeRs(ResultSet rs) {
        if(rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //关闭所有资源
    public static void closeAll(Connection conn,Statement stmt,ResultSet rs) {
        closeRs(rs);
        closeStmt(stmt);
        closeConn(conn);
    }
    private static String getValue(String key) {
        if("".equals(key)||key==null)
            throw new NullPointerException("key值有误");
        return pro.getProperty(key);
    }
}


总结


1:通过Properties来加载配置文件信息。
2:通过getContextClassLoader().getResourceAsStream()读取相对于项目根目录下的资源文件。(这里的ClassLoader是一个上下文的加载器)。
3:在获取Properties中的值时,通过异常中断去处理了程序
4:通过静态代码块保证加载驱动只需要执行一次