初识JDBC

一、JDBC概述

1.1数据的持久化

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各种关系数据库来完成。

持久化的主要应用是将内存中的数据存储在关系型数据库中,当然也可以存储在磁盘文件、XML数据文件中。 

1.2Java中的数据存储技术

①JDBC直接访问数据库

②JDO (Java Data Object )技术

③第三方O/R工具,如Hibernate, Mybatis 等

④JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。

1.3JDBC介绍

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API),定义了用来访问数据库的标准Java类库,(java.sql,javax.sql)使用这些类库可以以一种标准的方法、方便地访问数据库资源。

JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。

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

如果没有JDBC,那么Java程序访问数据库时是这样的:

有了JDBC,Java程序访问数据库时是这样的: 

 

总结如下:

1.4 JDBC体系结构

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

面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果)。

面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用。

1.5 JDBC程序编写步骤

 补充:ODBC(Open Database Connectivity,开放式数据库连接),是微软在Windows平台下推出的。使用者在程序中只需要调用ODBC API,由 ODBC 驱动程序将调用转换成为对特定的数据库的调用请求。

二、获取数据库连接

2.1 要素一:Driver接口实现类

2.1.1 Driver接口介绍

java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口。这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现。

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

例如:

Oracle的驱动:oracle.jdbc.driver.OracleDriver,

mySql的驱动: com.mysql.jdbc.Driver。

 将上述jar包拷贝到Java工程的一个目录中,习惯上新建一个lib文件夹。

在驱动jar上右键-->Build Path-->Add to Build Path:

 注意:如果是Dynamic Web Project(动态的web项目)话,则是把驱动jar放到WebContent(有的开发工具叫WebRoot)目录中的WEB-INF目录中的lib目录下即可。

2.1.2 加载与注册JDBC驱动

加载驱动:加载 JDBC 驱动需调用 Class 类的静态方法 forName(),向其传递要加载的 JDBC 驱动的类名——Class.forName(“com.mysql.jdbc.Driver”);。

注册驱动:DriverManager 类是驱动程序管理器类,负责管理驱动程序。

通常不用显式调用 DriverManager 类的 registerDriver() 方法来注册驱动程序类的实例,因为 Driver 接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用 DriverManager.registerDriver() 方法来注册自身的一个实例。下图是MySQL的Driver实现类的源码:

2.2 要素二:URL

JDBC URL 用于标识一个被注册的驱动程序,驱动程序管理器通过这个 URL 选择正确的驱动程序,从而建立到数据库的连接。

2.2.1JDBC URL的标准由三部分组成,各部分间用冒号分隔:

jdbc:子协议:子名称

协议:JDBC URL中的协议总是jdbc 

子名称:一种标识数据库的方法。子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息。包含主机名(对应服务端的ip地址),端口号,数据库名

举例:

2.2.2几种常用数据库的 JDBC URL

MySQL的连接URL编写方式:

jdbc:mysql://主机名称:mysql服务端口号/数据库名称?参数=值&参数=值

jdbc:mysql://localhost:3306/test

jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8(如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)

jdbc:mysql://localhost:3306/test?user=root&password=123456

Oracle 9i的连接URL编写方式:

jdbc:oracle:thin:@主机名称:oracle服务端口号:数据库名称

jdbc:oracle:thin:@localhost:1521:test

 SQLServer的连接URL编写方式:

jdbc:sqlserver://主机名称:sqlserver服务端口号:DatabaseName=数据库名称

jdbc:sqlserver://localhost:1433:DatabaseName=atguigu

2.3 要素三:用户名和密码 

user,password可以用“属性名=属性值”方式告诉数据库;

可以调用 DriverManager 类的 getConnection() 方法建立到数据库的连接。

三、使用PreparedStatement实现CRUD操作

3.1 操作和访问数据库

数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。

在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

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

PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。

CallableStatement:用于执行 SQL 存储过程:

3.2 使用Statement操作数据表的弊端

通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。

Statement 接口中定义了下列方法用于执行 SQL 语句:

int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE;
ResultSet executeQuery(String sql):执行查询操作SELECT。

但是使用Statement操作数据表存在弊端:

问题一:存在拼串操作,繁琐;

问题二:存在SQL注入问题。

可行方式:

3.3PreparedStatement的使用

3.3.1 PreparedStatement介绍

可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象。

PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句。

PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值。

3.3.2 PreparedStatement vs Statement

①用 PreparedStatement写的代码具有更好的可读性和可维护性。

②PreparedStatement 能最大可能提高性能:

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

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

(语法检查,语义检查,翻译成二进制命令,缓存)。

③PreparedStatement 可以防止 SQL 注入 。 

3.3.3 Java与SQL对应数据类型转换表

3.3.4 使用PreparedStatement实现增、删、改操作

/*
*使用PreparedStatement来替换Statement,实现对数据表的增删改操作
*
*/
public class PreparedStatementUpdateTest {
    @Test
    public void testCommonUpdate() {
        String sql = "delete from customers where id = ?";
        update(sql,3);
    }
//    通用的增删改操作
    public void update(String sql,Object ...args) {//sql中占位符的个数与可变形参的长度一致!
        Connection conn = null;
        PreparedStatement ps = null;
        try {
//        1.获取数据库连接
            conn = JDBCUtils.getConnection();
//        2.预编译sql语句,返回PreparedStatement的实例
            ps = conn.prepareStatement(sql);
//        3.填充占位符
            for (int i = 0;i < args.length;i++) {
                ps.setObject(i+1,args[i]);//小心参数声明!
            }
//        4.执行
            ps.execute();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
//        5.资源的关闭
            JDBCUtils.closeResource(conn,ps);
        }
    }

3.3.5 使用PreparedStatement实现查询操作

/*
*使用PreparedStatement实现针对于不同表的通用的查询操作
*/
public class PreparedStatementQueryTest {
    @Test
    public void testGetInstance() {
        String sql = "select id,name,email from customers where id = ?";
        Customer customer = (Customer) getInstance(Customer.class, sql, 12);
        System.out.println(customer);

        String sql1 = "select order_id orderId,order_name orderName from `order` where order_id = ?";
        Order order = (Order) getInstance(Order.class, sql1, 1);
        System.out.println(order);
    }
    @Test
    public void testGetForList() {
        String sql = "select id,name,email from customers where id < ?";
        List<Customer> list = getForList(Customer.class, sql, 12);
        list.forEach(System.out::println);

        String sql1 = "select order_id orderId,order_name orderName from `order` where order_id < ?";
        List<Order> list1 = getForList(Order.class, sql1, 4);
        list1.forEach(System.out :: println);
    }
    public <T> List<T> getForList(Class<T> clazz,String sql,Object ...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0;i < args.length;i++) {
                ps.setObject(i+1,args[i]);
            }
            rs = ps.executeQuery();
//            获取结果集元数据:ResultSetMetaData
            ResultSetMetaData rsmd = rs.getMetaData();
//            通过ResultSetMetaData获取结果集中的列数
            int columnCount = rsmd.getColumnCount();
//            创建集合对象
            ArrayList<T> list = new ArrayList<>();
            while(rs.next()) {
                T t = clazz.newInstance();
//                处理结果集一行数据中的每一列:给t对象指定的属性赋值
                for (int i = 0;i < columnCount;i++) {
//                    获取列值
                    Object columnValue = rs.getObject(i+1);
//                    获取每个列的列名
                    String columnLabel = rsmd.getColumnLabel(i + 1);
//                    给t对象指定的columnName属性,赋值为columnValue:通过反射
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                list.add(t);
            }
            return list;
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

3.4 ResultSet与ResultSetMetaData

3.4.1 ResultSet

查询需要调用PreparedStatement 的 executeQuery() 方法,查询结果是一个ResultSet 对象。

ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现。

ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。

ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。相当于Iterator对象的 hasNext() 和 next() 方法的结合体。

当指针指向一行时, 可以通过调用 getXxx(int index) 或 getXxx(int columnName) 获取每一列的值。

3.4.2 ResultSetMetaData

可用于获取关于 ResultSet 对象中列的类型和属性信息的对象。

ResultSetMetaData meta = rs.getMetaData();

getColumnName(int column):获取指定列的名称。

getColumnCount():返回当前 ResultSet 对象中的列数。

getColumnTypeName(int column):检索指定列的数据库特定的类型名称。

getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。

isNullable(int column):指示指定列中的值是否可以为 null。 

isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。 

3.5 资源的释放

释放ResultSet, Statement,Connection。

数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是**尽量晚创建,尽量早的释放。

可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。

3.6 JDBC API小结

两种思想:

①面向接口编程的思想;

②ORM思想(object relational mapping):

一个数据表对应一个java类;

表中的一条记录对应java类的一个对象;

表中的一个字段对应java类的一个属性;

sql是需要结合列名和表的属性名来写。注意起别名。

两种技术:

①JDBC结果集的元数据:ResultSetMetaData:

获取列数:getColumnCount();

获取列的别名:getColumnLabel()。

②通过反射,创建指定类的对象,获取指定的属性并赋值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值