当前位置:首页 » 《关注互联网》 » 正文

JDBC相关介绍、操作访问数据库_South.return的博客

16 人参与  2022年01月21日 11:09  分类 : 《关注互联网》  评论

点击全文阅读


目录

JDBC相关介绍

数据的持久化

数据库存储技术

数据访问

体系结构

编写步骤

获取数据库连接

要素一:Driver接口实现类

要素二:URL

要素三:用户名和密码

具体实现

操作和访问数据库

图解

数据库的调用方式

ResultSet

ResultSetMetaData

资源的释放

代码实现:通用的增删改操作

代码实现:通用的查询操作

总结

两种思想

两种技术

JDBC相关介绍


概述:JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库的公共接口(一组API)。

数据的持久化

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用,持久化的实现过程大多通过各种关系数据库来完成

数据库存储技术

在Java中,数据库存取技术可分为如下几类:
1. JDBC直接访问数据库
2. JDO(Java Data Object )技术
3. 第三方O/R工具,如Hibernate, Mybatis等

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

数据访问

没有JDBC,java程序访问数据库
有了JDBC,java程序访问数据库
总结

体系结构

JDBC接口(API)包括两个层次:
1. 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果
2. 面向数据库的API:Java Driver API,供开发商开发数据库驱动程序用

JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。

不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。(面向接口编程)

编写步骤

说明:ODBC(Open Database Connectivity,开放式数据库连接)

获取数据库连接


要素一:Driver接口实现类

Driver接口

java.sql.Driver接口是所有JDBC驱动程序需要实现的接口

这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现

在程序中不需要直接去访问实现了Driver接口的类

而是由驱动程序管理器类(java.sql.DriverManager)去调用这些Driver实现

Oracle的驱动:oracle.jdbc.driver.OracleDriver
mySql的驱动:com.mysql.jdbc.Driver
加载驱动加载JDBC驱动需调用Class类的静态方法forName(),向其传递要加载的JDBC驱动的类名
Class.forName(“com.mysql.jdbc.Driver”)
注册驱动DriverManager类是驱动程序管理器类,负责管理驱动程序
使用DriverManager.registerDriver(com.mysql.jdbc.Driver)来注册驱动

通常不用显式调用DriverManager类的registerDriver()方法来注册驱动程序类的实例

因为Driver接口的驱动程序类都包含了静态代码块,在这个静态代码块中,会调用

DriverManager.registerDriver()方法来注册自身的一个实例

要素二:URL

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

JDBC URL的标准由三部分组成,各部分间用冒号分隔
     jdbc:子协议:子名称
     协议:JDBC URL中的协议总是jdbc
    子协议:子协议用于标识一个数据库驱动程序
    子名称:一种标识数据库的方法

子名称可以依不同的子协议而变化,用子名称的目的是为了定位数据库提供足够的信息

包含主机名(对应服务端的ip地址),端口号,数据库名

图解

常用数据库的JDBC URL

MySQL的连接URL编写

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

2. jdbc:mysql://localhost:3306/test

3. jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8

如果JDBC程序与服务器端的字符集不一致,会导致乱码

可以通过参数指定服务器端的字符集

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

Oracle 9i的连接URL编写

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

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

SQLServer的连接URL编写

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

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

要素三:用户名和密码

  • user,password可以用"属性名=属性值"方式告诉数据库
  • 可以调用DriverManager类的getConnection()方法建立到数据库的连接

具体实现

使用配置文件的方式保存配置信息,在代码中加载配置文件
使用配置文件的好处实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码
如果修改了配置信息,省去重新编译的过程
配置文件设计

 因为操作数据库时会多次获取连接,所以就把获取连接和关闭资源的功能代码放在工具类中。

package util;

//导包
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

//操作数据库的工具类
public class JDBCUtils {

    //获取数据库的连接
    public static Connection getConnection() throws Exception {
        //1.读取配置文件中的4个基本信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");

        //文件内容读到集合
        Properties prop = new Properties();
        prop.load(is);

        //获取对应的值
        String user = prop.getProperty("user");
        String password = prop.getProperty("password");
        String url = prop.getProperty("url");
        String driverClass = prop.getProperty("driverClass");

        //2.加载驱动
        Class.forName(driverClass);

        //3.获取连接
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }

    //关闭资源
    public static void closeResource(Connection conn, Statement ps){
        try {
            if (ps != null)
                ps.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            if (conn != null)
                conn.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    //关闭资源
    public static void closeResource(Connection conn, Statement ps,ResultSet rs){
        try {
            if (ps != null)
                ps.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            if (conn != null)
                conn.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }try {
            if (rs != null)
                rs.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

操作和访问数据库


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

图解

数据库的调用方式

Statement概述:用于执行静态SQL语句并返回它所生成结果的对象
 接口中定义了的方法

int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE

ResultSet executeQuery(String sql):执行查询操作SELECT

弊端

存在拼串操作,繁琐

说明:"SELECT user,password FROM user_table

WHERE user = '"+ user +"' AND password = '"+ password +"'"

存在SQL注入问题

说明:SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查

而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password

FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') 

从而利用系统的SQL引擎完成恶意行为的做法

PrepatedStatement概述:SQL语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句
介绍:PreparedStatement接口是Statement的子接口,它表示一条预编译过的SQL语句
创建:可以通过调用Connection对象的preparedStatement(String sql)方法获取PreparedStatement对象
使用

PreparedStatement对象所代表的SQL语句中的参数用问号(?) 来表示

调用PreparedStatement对象的setObject()方法来设置这些参数

setObject()方法有两个参数

第一个参数是要设置的SQL语句中的参数的索引(从1开始)

第二个是设置的SQL语句中的参数的值

PreparedStatement  VS  Statement

1. PreparedStatement可以操作Blob的数据,Statement做不到

2. PreparedStatement能实现更高效的批量操作,最大可能提高性能

3. PreparedStatement可以解决Statement拼串和防止SQL注入 

CallableStatement概述:用于执行SQL存储过程
图解

ResultSet

说明:查询需要调用PreparedStatement的executeQuery()方法,查询结果是一个ResultSet对象
介绍ResultSet对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet接口由数据库厂商提供实现
ResultSet返回的实际上就是一张数据表,有一个指针指向数据表的第一条记录的前面
使用

ResultSet对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前

可以通过ResultSet对象的next()方法移动到下一行。调用next()方法检测下一行是否有效

若有效,该方法返回true,且指针下移。相当于Iterator对象的hasNext()和next()方法的结合体

当指针指向一行时, 可以通过调用getXxx(int index)或getXxx(int columnName)获取每一列的值
例如: getInt(1), getString("name")
注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始
常用方法- boolean next()
- getString()
图解

Java与SQL

对应数据类型转换表

ResultSetMetaData

说明可用于获取关于ResultSet对象中列的类型和属性信息的对象
ResultSetMetaData meta = rs.getMetaData();
常用方法getColumnName(int column):获取指定列的名称
getColumnLabel(int column):获取指定列的别名
getColumnCount():返回当前ResultSet对象中的列数
getColumnTypeName(int column):检索指定列的数据库特定的类型名称 
getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位 
isNullable(int column):指示指定列中的值是否可以为null
isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的
注意

表的字段名与类的属性名不相同的情况:

1. 必须声明sql时,使用类的属性名来命名字段的别名
2. 使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName()获取列的别名

说明:如果sql中没有给字段起别名,getColumnLabel()获取的就是列名

图解

资源的释放

释放ResultSet,、Statement、Connection

数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机

Connection的使用原则是尽量晚创建,尽量早的释放

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

代码实现:通用的增删改操作

说明:获取数据库连接和资源关闭使用的是之前创建的工具类中的方法。

    //通用的增删改操作
    //sql中占位符的个数与可变形参的长度相同!
    public void update(String sql,Object ...args) {
        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);
        }
    }

代码实现:通用的查询操作

说明:查询操作有返回值,我们一般使用创建对象,再给对象赋值的方式输出结果。

//结果集的输出方式
//方式一:
//System.out.println("id="+id+" name="+name+" email="+email+" Date:"+birth);
//方式二:
//Object[] date = new Object[]{id,name,email,birth};
//方式三:将数据封装为一个对象(推荐)

查询一条记录:

    //针对于不同表的通用查询操作,返回表中一条记录
    public <T> T getInstance(Class<T> clazz,String sql,Object ...args){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //获取连接
            conn = JDBCUtils.getConnection();
            //预编译sql语句,返回PreparedStatement实例
            ps = conn.prepareStatement(sql);
            //填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }

            //执行:捕获结果集
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();
            if (rs.next()){
                //创建此Class对象所表示类的实例
                T t = clazz.newInstance();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++) {
                    //获取每个列的列值:通过ResultSet
                    Object columnValue = rs.getObject(i + 1);

                    //获取每个列的列名:通过ResultSetMetaData
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    //通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                return t;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //释放资源
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

 查询多条记录:

    //针对于不同表的通用查询操作,查询多条记录
    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();
            //预编译sql语句,返回PreparedStatement实例
            ps = conn.prepareStatement(sql);
            //填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }

            //执行:捕获结果集
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();
            //创建集合对象
            ArrayList<T> list = new ArrayList<>();
            while (rs.next()){
                //创建此Class对象所表示类的实例
                T t = clazz.newInstance();
                //处理结果集一行数据中的每一个列:给t对象指定的属性赋值
                for (int i = 0; i < columnCount; i++) {
                    //获取每个列的列值:通过ResultSet
                    Object columnValue = rs.getObject(i + 1);

                    //获取每个列的列名:通过ResultSetMetaData
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    //通过反射,将对象指定名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;
    }

总结


两种思想

  • 面向接口编程的思想
  • ORM思想(object relational mapping)
        一个数据表对应一个java类
        表中的一条记录对应java类的一个对象
        表中的一个字段对应java类的一个属性

两种技术

  • JDBC结果集的元数据:ResultSetMetaData
        获取列数:getColumnCount()
        获取列的别名:getColumnLabel()
  • 通过反射,创建指定类的对象,获取指定的属性并赋值



点击全文阅读


本文链接:http://zhangshiyu.com/post/33653.html

数据库  获取  对象  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1