java学习-JDBC

 

一、Java中数据库存储技术

1、在java中,数据库存储技术,可以分为:

1)、JDBC直接访问数据库;

2)、JDO技术;

3) 、第三方O/R工具,如:hibernate、mybatis。

2、JDBC是访问数据库的基石,JDO/hibernate只是更好的封装了JDBC。

 

二、JDBC基础

1、JDBC(java database connectivity)是一个独立于特定数据库管理系统、通用的Sql数据库存取和操作的公共接口,定义了用来访问数据库的标准java类库,使用这个类库可以用一种标准的方法,方便的访问数据库资源;

2、JDBC为访问不同的数据库提供了同意的途径,未开发这屏蔽了一些细节问题;

3、JDBC的目标是使java程序开发者使用jdbc可以连接任何提供了JDBC的数据库系统,这样就是的程序员无需对特定的数据库系统的特点有过多的了解,从而简化了开发流程。

下面用两张图理解下JDBC:

 

三、JDBC的体系结构

JDBC接口(API)包括两个层次:

1、面向应用的API:java API、抽象接口、应用程序开发者使用,主要用于连接数据库、执行Sql语句,获得结果;

2、面向数据库的API:java Driver API,供开发商开发数据库驱动程序。

如图理解一下:

 

 

四、JDBC驱动程序分类

定义:JDBC驱动程序就是各个数据库厂商根据JDBC规范制作的JDBC实现类的类库;

JDBC驱动程序总共有四种类型:

1)、JDBC-ODBC桥;

2)、部分本地API,部门java的驱动程序;

3)、JDBC网络纯Java驱动程序;

4)、本地协议的纯java驱动程序。

第三和第四都是纯java的驱动程序,因此,对于java开发者来说,他们在性能、可移植性、功能等方面具有优势。

a.JDBC-ODBC桥

1、JDBC-ODBC桥本身也是一种驱动,利用这个驱动,可以使用JDBC-API通过ODBC去访问数据库。这种其实是把标准的JDBC调用转化成ODBC调用,并通过ODBC访问数据库;

2、要通过多层访问数据库,所以ODBC访问数据库,效率低下;

b.部分本地API,部分Java的驱动程序

1、这种类型的驱动程序是通过java编写,调用厂商提供的API;

2、通过这种驱动程序访问数据库,减少了ODBC的调用环节,提高了数据库的访问效率;

3、需要在客户的机器上安装本地JDBC驱动程序和特定厂商的API.

 

c.JDBC网络纯JAVA驱动程序

1、这种驱动利用中间件的应用服务器来访问数据库。应用服务器作为一个或者多个数据库的网关,客户端通过他可以连接到不同的数据库服务器;

2、应用程序通常有自己的网络协议,java用户程序通过jdbc驱动程序将jdbc调用发送给应用服务器,应用服务器通过本地程序驱动访问数据库,完成请求。

d.本地协议的纯java驱动程序

1、多数数据库厂商已经支持允许客户程序通过网络直接与数据库通信的网络协议;

2、这种类型的驱动程序完全使用 Java 编写,通过与数据库建立的 Socket 连接,采用具体与厂商的网络协议把 JDBC 调用转换为直接连接的网络调用;

 

 

五、各类接口

Driver

1、Java.Sql.Driver实现的接口是所有的jdbc驱动程序需要实现的接口,这个接口是提供给数据库厂商使用的,不同的数据库厂商提供不同的实现

2、在程序中不需要去访问实现了Driver接口的类,而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver的实现;

3、 加入 mysql 驱动方法 

 1). 解压 mysql-connector-java-5.1.7.zip(可以自行到数据库官网下载
 2). 在当前项目下新建 lib 目录
 3). 把mysql-connector-java-5.1.7-bin.jar 复制到 lib 目录下
 4). 右键 build-path , add tobuildpath 加入到类路径下.s

public void testDriver() throws SQLException {
        // 1.创建一个Driver实现类的对象
        Driver driver = new com.mysql.jdbc.Driver();

        // 2. 准备连接数据库的基本信息: url, user, password
        String url = "jdbc:mysql://localhost:3306/test";
        Properties info = new Properties();
        info.put("user", "root");
        info.put("password", "123456a?");

        // 3. 调用 Driver 接口的 connect(url, info) 获取数据库连接
        Connection connection = driver.connect(url, info);
        System.out.println(connection);
    }

写一个扩展性好的数据库连接串:

上面这段代码把连接数据库的四个参数url、driver、user、pasword放在了程序中,其实在真正的工程文件中,如果这么使用,那么程序的扩展性就太差了,那么我们的方法是:将这四个连接数据库的变量放在一个独立的配置文件中(jdbc.properties),这样,我们可以获得任何数据库的连接,不仅仅局限于MySql,例如下面的配置文件和代码片段:

jdbc.properties配置文件:

driver=com.mysql.jdbc.Driver   //数据库驱动
jdbcUrl=jdbc:mysql://localhost:3306/xiaopStudy   //数据库连接串
user=root         //数据库账户
password=password       //数据库密码

Driver连接数据库代码片段:

public Connection getConnection() throws Exception {
        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");

        // 通过反射常见 Driver 对象.
        Driver driver = (Driver) Class.forName(driverClass).newInstance();
        Properties info = new Properties();
        info.put("user", user);
        info.put("password", password);
        // 通过 Driver 的 connect 方法获取数据库连接.
        Connection connection = driver.connect(jdbcUrl, info);

        return connection;
    }

DriverManager

1、加载JDBC驱动,需要调用Class的静态方法forName(),参数是要加载的JDBC驱动的类名;

2、DriverManager是驱动程序管理器类,用来管理驱动程序;

3、通常不用显式的调用DiverManager类的registerDrive方法来注册驱动程序类的实例,因为Driver接口的驱动程序类都包含了静态代码块,在这个静态代码中,会调用DriverManager.registerDriver()方法来注册自身的一个实例;

4、可以调用DriverManager的getConnection()的方法建立数据库的连接;

5、可以同时管理多个驱动程序: 若注册了多个数据库连接, 则调用 getConnection() 方法时传入的参数不同, 即返回不同的数据库连接。

public static Connection getConnection() throws Exception{
        
        //driver/jdbcUrl/user/password
        Properties pro = new Properties();
        Connection connection  =null;
        InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
        pro.load(in);
        String driver = pro.getProperty("driver");
        String jdbcUrl = pro.getProperty("jdbcUrl");
        String user = pro.getProperty("user");
        String password = pro.getProperty("password");
        
        //加载数据库驱动程序
        Class.forName(driver);
        
        //通过DriverManager的getConnection方法获取数据库连接
        connection = DriverManager.getConnection(jdbcUrl, user, password);
        return connection;
    }

Statement

1、Statement用于执行sql语句的对象

  1).通过Connection的createStatement对象来获取 ;

  2).通过executeUpdate(sql)来执行sql语句;

  3).传入的sql语句可以是INSERT/UPDATE/DELETE,但不能是SELECT;

2、Connection和Statement都是数据库连接的资源,用完之后都要在finally中关闭;

3、关闭的顺序是:先关闭后获取的,即先关闭Statement,在关闭Connection

在这个方法中,可以将finally中关闭Statement和Connection的代码独立成一个方法,这也在下面的代码中有所体现:

public void testStatement() throws Exception {
        // 1. 获取数据库连接
        Connection connection = null;
        Statement statement = null;

        try {
            // 3. 准备插入的 SQL 语句

            String sql = "INSERT INTO customers(NAME,email,birth) VALUES('Tim','Tim@xiaop.com',NOW())";
            // String sql = "update customers set name='LanLan' where id=2";
            // String sql = "delete from customers where id=3";
            // 4. 执行插入.
            // 1). 获取操作 SQL 语句的 Statement 对象:
            // 调用 Connection 的 createStatement() 方法来获取
            connection = getConnectionByDriverManager();
            statement = connection.createStatement();

            // 2). 调用 Statement 对象的 executeUpdate(sql) 执行 SQL 语句进行插入
            statement.executeUpdate(sql);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 5. 关闭 Statement 对象.
            release(statement, connection);

        }

    }


/**
     * 关闭Statement和Connection的数据库连接
     * @param statement
     * @param connection
     */
    public static void release(Statement statement,Connection connection){
        if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
    }

ResultSet: 结果集.

封装了使用 JDBC 进行查询的结果

1. 调用 Statement 对象的 executeQuery(sql)可以得到结果集;

2. ResultSet 返回的实际上就是一张数据表. 有一个指针指向数据表的第一样的前面. 可以调用 next()方法检测下一行是否有效. 若有效该方法返回 true, 且指针下移. 相当于 Iterator 对象的 hasNext() 和 next()方法的结合体;

3. 当指针对位到一行时, 可以通过调用 getXxx(index) 或 getXxx(columnName) 获取每一列的值,例如: getInt(1), getString("name")

4. ResultSet 当然也需要进行关闭.

public void testResultSet() {
        Connection connection = null;
        Statement statement = null;
        ResultSet rs = null;
        try {
            // 1. 获取 Connection
            connection = JDBCUtil.getConnection();
            System.out.println(connection);

            // 2. 获取 Statement
            statement = connection.createStatement();
            System.out.println(statement);

            // 3. 准备 SQL
            String sql = "select id,name,email,birth from customers";

            // 4. 执行查询, 得到 ResultSet
            rs = statement.executeQuery(sql);
            System.out.println(rs);

            // 5. 处理 ResultSet
            while (rs.next()) {
                int id = rs.getInt(1);
                String name = rs.getString(2);
                String email = rs.getString(3);
                Date birth = rs.getDate(4);

                // String id = rs.getString("id");
                // String name = rs.getString("name");
                // String email = rs.getString("email");
                // String birth = rs.getString("birth");

                System.out.println(id);
                System.out.println(name);
                System.out.println(email);
                System.out.println(birth);
                System.out.println("*************************************");

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭数据库资源.
            release(statement, connection, rs);
        }

    }



/**
     * 关闭statement、connection、resultset
     * @param statement
     * @param connection
     * @param rs
     */
    public static void release(Statement statement,Connection connection,ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(statement!=null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection !=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

 PreparedStatement

1、通过Connection的preparedStatement()方法获取到PreparedStatement对象;

2、PreparedStatement接口是Statement的子接口,它表示一条预编译过的sql语句;

3、PreparedStatement对象所代表的sql语句中的参数用?来表示,调用PreparedStatement对象的setXXX()方法来设置参数值,setXXX(param1,param2)方法有两个参数,param1表示sql语句中参数的索引,从1开始,第二个参数param2是Sql语句中参数的值。

PreparedStatement vs Statement

1、代码的可读性和可维护性;

2、DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。

在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.
(语法检查,语义检查,翻译成二进制命令,缓存)

3、PreparedStatement 可以防止 SQL 注入

PreparedStatement 参考代码:

public void testPerparedStatement() throws Exception{
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            String sql = "insert into customers(name,email,birth) VALUES(?,?,?)";
            conn = JDBCUtil.getConnection();
            ps = conn.prepareStatement(sql);
            ps.setString(1, "zzq");
            ps.setString(2, "1554300832@qq.com");
            ps.setDate(3, new Date(new java.util.Date().getTime()));
            
            ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            JDBCUtil.release(null, ps, conn);
        }
    }

PreparedStatement 与Statement防止sql注入比较:

/**
     * PreparedStatement可以防止Sql注入
     * @throws Exception
     */
    @Test
    public void testSqlInjection2() throws Exception{
        String username = "a' OR PASSWORD = ";
        String password = " OR '1'='1";
        
        Connection connection  = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        
        try {
            String sql="SELECT NAME,email,birth FROM customers WHERE NAME=? AND PASSWORD=?";
                    
            System.out.println(sql);
            connection = JDBCUtil.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, username);
            preparedStatement.setString(2, password);
            
            resultSet = preparedStatement.executeQuery();
            if(resultSet.next()){
                System.out.println("登录成功");
            }else{
                System.out.println("用户名和密码不正确");
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            JDBCUtil.release(resultSet, preparedStatement, connection);
        }
    }
    
    /**
     * Statement不能防止Sql注入
     * @throws Exception
     */
    @Test
    public void testSqlInjection() throws Exception{
        //String username = "Tom";
        //String password = "1234561";
        
        String username = "a' OR PASSWORD = ";
        String password = " OR '1'='1";
        
        String sql = "SELECT NAME,email,birth FROM customers WHERE"+
                " NAME='"+username+"' and PASSWORD='"+password+"'";
        System.out.println(sql);
        
        Connection connection  = null;
        Statement statement = null;
        ResultSet resultSet = null;
        
        try {
            connection = JDBCUtil.getConnection();
            statement = connection.createStatement();
            resultSet = statement.executeQuery(sql);
            
            if(resultSet.next()){
                System.out.println("登录成功!");
            }else{
                System.out.println("用户名和密码不匹配或用户名不存在.");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally{
            JDBCUtil.release(resultSet, statement, connection);
        }
        
    }

 

posted @ 2017-09-04 16:02  飞鸿踏雪泥xp  阅读(231)  评论(0编辑  收藏  举报