一、相关概念

1.JDBC的定义

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

  JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,使程序员无需对特定的数据库系统的特点有过多的了解,大大简化和加快了开发过程。

2.数据库驱动

  我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。

二、常用接口

1.Driver接口

  Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:

  装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");

  装载Oracle驱动:Class.forName("oracle.jdbc.driver.OracleDriver");

  DriverManager是驱动的管理类,可以通过重载的getConnection()方法获取数据库连接,较为方便;可以同时管理多个驱动程序。 

2.Connection接口

  Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。

  连接MySql数据库:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");

  连接Oracle数据库:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");

常用方法:

  createStatement():创建向数据库发送sql的statement对象。

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

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

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

  commit() :在链接上提交事务。

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

3.Statement接口

  用于执行静态SQL语句并返回它所生成结果的对象。

三种Statement类:

  Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。

  PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。

  CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。

常用Statement方法:

  execute(String sql):运行语句,返回是否有结果集

  executeQuery(String sql):运行select语句,返回ResultSet结果集。

  executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。

  addBatch(String sql) :把多条sql语句放到一个批处理中。

  executeBatch():向数据库发送一批sql语句执行。

4.ResultSet接口

ResultSet提供检索不同类型字段的方法,常用的有:

  getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。

  getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。

  getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。

  getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。

  getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。

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

  next():移动到下一行

  Previous():移动到前一行

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

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

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

三、使用JDBC的步骤

1.注册驱动并获取数据库连接

/**
     * 编写一个通用的方法,在不修改源程序的情况下,可以获取任何 数据库的连接
     * 解决办法:把数据库驱动Driver实现类的全类名、url、user、password放入
     * 一个配置文件中,通过修改配置文件的方式实现具体数据库的连接
     */
    public Connection getConnection() throws Exception{
        //1.准备连接数据库的基本信息
        String driverClass = null;
        String jdbcUrl = null;
        String user = null;
        String password = null;
        //读取类路径下的jdbc.properties文件
        InputStream in = getClass().getClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(in);
        driverClass = properties.getProperty("driver");
        jdbcUrl = properties.getProperty("jdbcUrl");
        user = properties.getProperty("user");
        password = properties.getProperty("password");
        
        //2.加载数据库驱动程序(对应的Driver实现类中有注册驱动的静态代码块,所以不用下面的方法,否则会产生两个一样的驱动)
        //DriverManager
        //    .registerDriver(Class.forName(driverClass).newInstance));
        Class.forName(driverClass);
        //3.通过DriverManager的getConnection()方法 获取数据库连接
        Connection conn = DriverManager.getConnection(jdbcUrl, user, password);
        return conn;
    }

jdbc.properties文件:

driver=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/test
user=root
password=1230

#driver=oracle.jdbc.driver.OracleDriver
#jdbcUrl=jdbc:oracle:thin:@localhost:1521:orcl
#user=scott
#password=tiger

注:

 URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:

  

 其他参数如:useUnicode=true&characterEncoding=utf8

 2.创建执行SQL语句的statement

/**
     * 通过JDBC向指定的数据表中插入一条记录
     */
    public void testStatement(){
        Connection conn = null;
        Statement stat = null;
        try {
            //1.获取数据库连接
            conn = getConnection();
            //2.准备插入记录的SQL语句
            String sql = "INSERT INTO customers (NAME, EMAIL, BIRTH) " + 
            "VALUES('XYZ', 'xyz@163.com','1999-11-11')";
            //3.执行语句
            //1)获取操作SQL语句的Statement 对象
            stat = conn.createStatement();
            //2)调用executeUpdate(sql)
            stat.executeUpdate(sql);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //4.关闭Statement对象
            if(stat != null){
                try {
                    stat.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            //5.关闭连接
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

 SQL注入:

//Statement  
String id = "5";
String sql = "delete from table where id=" +  id;
Statement st = conn.createStatement();  
st.executeUpdate(sql);  
//存在sql注入的危险
//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录

PreparedStatement:

  是statement的子接口,可以传入带占位符的SQL语句,并且提供了补充占位符变量的方法。

  对预编译语句提供性能优化,还可以防止SQL注入。 

//preparedStatement
String id = "5";
String sql = "delete from table where id= ?";
PreparedStatement ps = conn.prepareStatement(sql);
//setXxx(int index,Xxx value):把sql语句中第index个占位符替换为value,index从1开始。
ps.setString(1, id);
ps.executeUpdate();

 3.处理查询的执行结果(ResultSet)

    /**
     * 获取customers数据表的所有记录,并打印
     */
    public void testResultSet() {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
            // 1.获取数据库连接
            conn = getConnection();
            // 2.获取statement
            stat = conn.createStatement();
            // 3.准备sql
            String sql = "select id, name, email, birth from customers ";
            // 4.执行查询,得到ResultSet
            rs = stat.executeQuery(sql);
            // 5.处理ResultSet
            while (rs.next()) {
                int id = rs.getInt(1);
                String name = rs.getString("name");
                String email = rs.getString(3);
                Date birth = rs.getDate(4);

                System.out.println(id);
                System.out.println(name);
                System.out.println(email);
                System.out.println(birth);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.关闭连接
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stat != null) {
                try {
                    stat.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

注:

  ResultSet返回的实际上是一张数据表,有一个指针指向表的第一行的前面。

  调用 next() 方法可以检测下一行是否有效,若有效返回true,且指针下移。

  当指针对位到某一行时,可以通过 getXxx(index) 或 getXxx(columnName) 获取该行中每一列的值。

  ResultSet也需要关闭。