【JDBC】JDBC获取数据库连接、数据的增删改查操作

一、JDBC

JDBC(Java Database Connectivity,Java数据库连接) 是一套用于执行SQL操作的Java API,应用程序可以通过这套API连接到关系数据库,并使用SQL操作完成对数据库中数据的CRUD等操作,方便地管理数据库资源。
数据的持久化:将内存中的数据保存到可掉电式存储设备中以供使用。(如关系数据库、磁盘文件、XML文件)

1.JDBC API两种思想

(1)面向接口编程的思想
(2)ORM思想(object relational mapping,对象关系映射)
① 一个数据表对应一个java类
② 表中的一条记录对应java类的一个对象
③ 表中的一个字段对应java类的一个属性
sql是需要结合列名和表的属性名来写。注意起别名。

2.JDBC API两种技术

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

如果没有JDBC,Java程序访问数据库是这样的:
在这里插入图片描述
有了JDBC以后,Java程序是这样访问数据库的:
在这里插入图片描述

3.JDBC接口(API)的两个层次

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

4.JDBC常用API

JDBC API主要位于java.sql和javax.sql包下:
(1)Driver接口
每个驱动程序类必须实现的接口。 使用时必须将所使用的数据库驱动程序或类库加载到项目的classpath中(这里是指数据库的驱动的jar包)。
在这里插入图片描述
(2)DriverManager类
DriverManager类用于加载JDBC驱动并且创建与数据库的连接。定义了两个重要的静态方法:

static Connection getConnection(String url, String user, String password) :尝试建立与给定数据库URL的连接,返回连接对象
static void registerDriver(Driver driver):向DriverManager中注册给定的JDBC驱动程序 

通常不适用registerDriver(Driver driver)这种方式注册驱动,因为在驱动类com.mysql,.jdbc.Driver中有一段静态代码块,是向DriverManager注册一个Driver实例。通常加载数据库驱动使用Class类的静态方法forName()来实现。
(3)Connection接口
Connection接口代表Java程序和数据库的连接对象,只有获得该连接对象后,才能访问数据库,操作数据库。
(4)Statement接口
Statement接口用于执行静态SQL语句,返回一个结果对象。
(存在的弊端:① 字符串拼接,繁琐 ② 存在SQL注入问题,对数据库中的表恶意操作)

SQL注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句段或命令(如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1= 'AND password = 'OR ‘1’=‘1’),从而利用系统的SQL引擎完成恶意行为的做法。

如下,尽管输入的是不规范的SQL语句,但是也能查询到数据中表的数据:
在这里插入图片描述
(5)PreparedStatement接口
PreparedStatement是Statement的子接口,用于执行预编译的SQL语句。扩展了带有参数的SQL语句的执行操作,应用程序中的SQL语句可以使用占位符“?”来代替其参数,然后通过setXxx()方法为SQL语句的参数赋值。
部分方法:
在这里插入图片描述
(6)ResultSet接口
ResultSet用于保存JDBC执行查询时返回的结果集,该结果集封装在一个逻辑表格中。
部分方法:
在这里插入图片描述

5.JDBC程序编写步骤

在这里插入图片描述

二、获取数据库连接

Statement接口存在SQL注入问题,所以不使用,我们通常使用PreparedStatement接口来实现对数据库的操作。
在这里插入图片描述
1.配置文件:jdbc.properties
(mysql 8及以上com.mysql.cj.jdbc.Driver需要使用cj,jdbc:mysql://localhost:3306/test此处test是需要操作的数据库的名称)

user=root
password=12345678
driverClass=com.mysql.cj.jdbc.Driver	
url=jdbc:mysql://localhost:3306/test

2.将数据库连接的4个基本信息声明在配置文件中,通过读取配置文件获取连接

//方式5:最终版;将数据库连接的4个基本信息声明在配置文件中,通过读取配置文件获取连接
//实现了数据与代码的分离,解耦;如果需要修改配置文件信息,可以避免程序重新打包(部署到tomcat服务器上时)
@Test
public void test05() throws Exception {
    //1.读取配置文件(使用类的加载器读取配置文件)
    InputStream resource = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
    Properties properties = new Properties();
    properties.load(resource);
    String user = properties.getProperty("user");
    String password = properties.getProperty("password");
    String url = properties.getProperty("url");
    String driverClass = properties.getProperty("driverClass");
    //2.加载驱动
    Class clazz = Class.forName(driverClass);
    //3.获取连接
    Connection connect = DriverManager.getConnection(url, user, password);
    System.out.println(connect);
}

三、对数据的增删改操作

1.数据的操作都会涉及读取配置文件、加载驱动、获取连接和关闭连接等操作,所以可以将这些步骤定义在一个工具类中,避免代码冗余。 如下:

/**
 * 数据库表中数据操作的工具类
 * @author wds
 * @date 2021-12-11 10:30
 */
//数据库操作的工具类
public class JdbcUtils {
    //获取数据库的连接
    public static Connection getConnection() throws Exception {
        //1.读取配置文件(使用系统加载器读取配置文件)
        InputStream resource = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(resource);
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driverClass = properties.getProperty("driverClass");
        //2.加载驱动
        Class clazz = Class.forName(driverClass);
        //3.获取连接
        Connection connect = DriverManager.getConnection(url, user, password);
        return connect;
    }
    //关闭连接(Statement和Connection)
    public static void closeConnection(Statement ps, Connection connect){
        try {
            if(ps!=null){
                ps.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            if(connect!=null){
                connect.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    //关闭连接(Statement和Connection)
    public static void closeConnection(Statement ps, Connection connect, ResultSet rs){
        try {
            if(ps!=null){
                ps.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            if(connect!=null){
                connect.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            if(rs!=null){
                rs.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

2.将实现增、删、改的通用操作声明在一个方法中,直接调用方法实现对数据库操作
(若使用的表是使用SQL中的关键字声明的,则需要使用``将数据库表名括起来使用)
因为对数据增删改操作只需要返回操作是否成功,使用通用的方法可以定义,如下:

public class PreparedStatementTest {
    @Test
    public void deleteTest(){
    	//1.对customers表进行删除操作
//        String sql="delete from customers where id = ?";
//        updateTable(sql,3);
		//2.对order表进行更新操作
		//此处的表名order和数据库关键字(order by)一样,必须使用``括起来使用
        String sql = "update `order` set order_name = ? where order_id = ?";
        updateTable(sql,"DD","2");
        System.out.println("sql操作执行成功...");
    }
    //实现数据库“增、删、改”的通用操作
    public void updateTable(String sql,Object...args) {
        Connection connect = null;
        PreparedStatement ps = null;
        try {
            //1.获取数据库的连接
            connect = JdbcUtils.getConnection();
            //2.预编译sql语句,返回prepareStatement的实例
            ps = connect.prepareStatement(sql);
            //3.填充占位符
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            //4.执行sql
            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5.关闭资源
            JdbcUtils.closeConnection(ps,connect);
        }
    }
}

四、对数据的查询操作

Java与SQL对应数据类型关系表
在这里插入图片描述
对于数据的查询操作,ORM思想至关重要:

ORM(Object Relation Mapping):对象关系映射

  • 数据库中的一张表对应一个java Bean
  • 数据库中表的一个字段对应一个Java属性
  • 数据库中表的一条记录对应一个Java对象

数据表查询的流程:
在这里插入图片描述

public class Order {
    private int orderId;
    private String orderName;
    private Date orderDate;
    ...
}
public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birth;
	...
}

1.单条记录的查询

public class PreparStatement {
    @Test
    public void test(){
    /*
     针对于表的字段名与类的属性名不相同的情况:
     1.必须声明sql时,使用类的属性名来命名字段的别名
     2.使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName(),获取列的别名。
     说明:如果sql中没有给字段其别名,getColumnLabel()获取的就是列名(此处是指数据库表的字段名和类的属性名一致时)
     */
        String sql = "select id,name,email,birth from customers where id = ?  ";
        Customer customer = queryForTable(Customer.class, sql, 1);
        System.out.println(customer);

        sql = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?";
        Order order = queryForTable(Order.class, sql, 1);
        System.out.println(order);
    }
    //针对不同数据表的通用的单行查询操作
    public <T> T queryForTable(Class<T> clazz, String sql,Object...args) {
        Connection connect = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        try {
            //1.获取数据库的连接
            connect = JdbcUtils.getConnection();
            //2.预编译Sql
            ps = connect.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            //3.执行sql操作
            resultSet = ps.executeQuery();
            //查询结果集的元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            //查询结果集的列数
            int columnCount = metaData.getColumnCount();
            if(resultSet.next()){
                //newInstance()只能调用无参构造方法,创建当前类的对象
                T t = clazz.newInstance();
                //返回结果集中的每一个列
                for(int i=0;i<columnCount;i++){
                    //获取每个列的列值,通过resultSet
                    Object columnValue = resultSet.getObject(i + 1);
                    //通过ResultSetMetaData
                    //获取每个列的列名
                    String columnName = metaData.getColumnName(i + 1);
                    //获取每个列的别名
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //通过反射,将对象指定名getColumnName的属性设置为指定的属性值:columnValue
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.closeConnection(ps,connect,resultSet);
        }
        return null;
    }
}

查询结果:
在这里插入图片描述

2.多条记录的通用查询

public class PrepareStatement02 {
    @Test
    public void test(){
        /*
        针对于表的字段名与类的属性名不相同的情况:
        1.必须声明sql时,使用类的属性名来命名字段的别名
        2.使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName(),获取列的别名。
        说明:如果sql中没有给字段其别名,getColumnLabel()获取的就是列名(此处是指数据库表的字段名和类的属性名一致时)
        */
        String sql = "select id,name,email,birth from customers where id < ?";
        List list = queryForTable(Customer.class, sql, 12);
        list.forEach(System.out::println);
        
        //sql中where条件后面没有筛选条件时,查询所有的数据,参数此时可以不写
        sql = "select order_id orderId,order_name orderName,order_date orderDate from `order`";
        List list1 = queryForTable(Order.class, sql);
        list1.forEach(System.out::println);
    }
    //针对不同数据表的通用的多行查询操作
    public <T> List queryForTable(Class<T> clazz, String sql, Object...args) {
        Connection connect = null;
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        try {
            //1.获取数据库的连接
            connect = JdbcUtils.getConnection();
            //2.预编译Sql
            ps = connect.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            //3.执行sql操作
            resultSet = ps.executeQuery();
            //查询结果集的元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            //查询结果集的列数
            int columnCount = metaData.getColumnCount();
            ArrayList<T> list = new ArrayList<>();
            while (resultSet.next()){
                //newInstance()只能调用无参构造方法,创建当前类的对象
                T t = clazz.newInstance();
                //返回结果集中的每一个列,给指定的t的属性赋值
                for(int i=0;i<columnCount;i++){
                    //获取每个列的列值,通过resultSet
                    Object columnValue = resultSet.getObject(i + 1);
                    //通过ResultSetMetaData
                    //获取每个列的列名
                    String columnName = metaData.getColumnName(i + 1);
                    //获取每个列的别名
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //通过反射,将对象指定名getColumnName的属性设置为指定的属性值:columnValue
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                //处理完当前的一个完整对象之后,添加进集合list
                list.add(t);
            }
            //所有的操作结束之后,再返回list
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.closeConnection(ps,connect,resultSet);
        }
        return null;
    }
}

查询结果:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智商三岁半i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值