【JavaWeb】一篇文章复习JDBC、DAO及相关实现类(一)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【JavaWeb】一篇文章复习JDBC、DAO及相关实现类(一)

一、JDBC实现数据库的连接


1、IEDA引入JDBC.jar包


在工程目录下,创建一个lib文件夹,将jar包复制到该文件夹下,然后如图进行引入。


3ad8a5e6b7e15b319df0692f9ae47ce6_image-20220909113810598.png


2、连接方式


首先我们先写一个配置文件jdbc.properties


使用配置文件的好处:


实现了代码和数据的分离,如果需要修改配置信息,直接在配置文件中修改,不需要深入代码

如果修改了 配置信息,省去重新编译的过程。


user=root
password=sm1208
url=jdbc:mysql://localhost:3306/javaweb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
driverClass=com.mysql.cj.jdbc.Driver


package com.sun.jdbc;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
 * @author JumperSun
 * @date 2022-09-08-19:57
 */
public class ConnectionTest {
    // 非读取配置文件方式
    @Test
    public void testConnection1(){
        try {
            // 1.数据库连接的4个基本要素
            String url = "jdbc:mysql://localhost:3306/javaweb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
            String driverName = "com.mysql.cj.jdbc.Driver";
            String user = "root";
            String password = "sm1208";
            // 2.加载驱动(实例化Driver 注册驱动)
            Class.forName(driverName);
            // 3.获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            System.out.println(conn);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
  // 读取配置文件方式
    @Test
    public void testConnection2() throws Exception {
        // 1.加载配置文件
        InputStream is = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(is);
        // 2.读取配置信息
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");
        // 3.加载驱动
        Class.forName(driverClass);
        // 4.获取连接
        Connection conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
    }
}



3、JDBCUtils工具类封装数据库连接和关闭操作


package com.sun.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
 * @author JumperSun
 * @date 2022-09-08-20:53
 * 封装的JDBC工具类
 */
public class JDBCUtils {
    public static Connection getConnection() throws Exception {
        // 1.读取配置文件的信息
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
        Properties pros = new Properties();
        pros.load(is);
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        String url = pros.getProperty("url");
        String driverClass = pros.getProperty("driverClass");
        // 2.加载驱动
        Class.forName(driverClass);
        // 3.返回获取连接
        return DriverManager.getConnection(url, user, password);
    }
    public static void closeResources(Connection conn, PreparedStatement ps) {
        try {
            if (ps != null) {
                ps.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void closeResources(Connection conn, PreparedStatement ps, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (ps != null) {
                ps.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}


二、使用PreparedStatement实现CRUD操作


PreparedStatement是什么?


PreparedStatement用来执行SQL查询语句的API之一,数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。

数据库操作对象都有哪些?


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

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

CallableStatement:用于执行 SQL 存储过程

为什么使用PreparedStatement?


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

存在拼串操作、繁琐

存在SQL注入问题

PreparedStatement表示一条预编译过的 SQL 语句

能最大可能提高性能

能最大可能提高性能:

综上:


b9f21dd938953a814f92f62caf197734_image-20220909114829848.png


1、PreparedStatement实现增、删、改操作


// 通用的增、删、改操作(体现一:增、删、改 体现二:针对不同的表)
public boolean update(String sql,Object ...args) {
    Connection conn = null;
    PreparedStatement ps = null;
    boolean flag = false;
    try {
        // 1.获取数据库的连接
        conn = com.sun.jdbc.JDBCUtils.getConnection();
        // 2.获取PreparedStatement实例
        ps = conn.prepareStatement(sql);
        // 3.填充占位符
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i+1,args[i]);
        }
        // 4.执行sql语句
        flag = ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 5.关闭资源
        JDBCUtils.closeResources(conn,ps);
        return flag;
    }
}


2、PreparedStatement实现查询操作


// 通用的针对于不同表的查询:返回一个对象
public <T> T getBean(Class<T> clazz,String sql,Object ...args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        // 1.获取数据库连接
        conn = JDBCUtils.getConnection();
        // 2.预编译sql语句,得到PrepareStatement对象
        ps = conn.prepareStatement(sql);
        // 3.填充占位符
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i+1,args[i]);
        }
        // 4.执行executeQuery(),得到结果:ResultSet
        rs = ps.executeQuery();
        // 5.得到结果集的元数据:ResultSetMetaData
        ResultSetMetaData rsmd = rs.getMetaData();
        // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值
        int columnCount = rsmd.getColumnCount();
        if (rs.next()) {
            // 通过反射,创建指定类的对象,获取指定的属性并赋值
            T t = clazz.newInstance();
            for (int i = 0; i < columnCount; i++) { // 遍历每一个列
                // 获取列值
                Object columnval = rs.getObject(i + 1);
                // 获取列的别名:列的别名,使用类的属性名充当
                String columnLabel = rsmd.getColumnLabel((Integer) columnval);
                // 使用反射,给对象的相应属性赋值
                Field field = clazz.getDeclaredField(columnLabel);
                field.setAccessible(true);
                field.set(t,columnval);
            }
            return t;
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 7.关闭资源
        JDBCUtils.closeResources(conn,ps,rs);
    }
    return null;
}



// 通用的针对于不同表的查询:返回对象集合
public <T> List<T> getBeanList(Class<T> clazz, String sql, Object ...args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        // 1.获取数据库连接
        conn = com.sun.jdbc.JDBCUtils.getConnection();
        // 2.预编译sql语句,得到PrepareStatement对象
        ps = conn.prepareStatement(sql);
        // 3.填充占位符
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i+1,args[i]);
        }
        // 4.执行executeQuery(),得到结果:ResultSet
        rs = ps.executeQuery();
        // 5.得到结果集的元数据:ResultSetMetaData
        ResultSetMetaData rsmd = rs.getMetaData();
        // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值
        int columnCount = rsmd.getColumnCount();
        // 创建集合对象
        ArrayList<T> list = new ArrayList<>();
        while (rs.next()) {
            T t = clazz.newInstance();
            // 处理结果集一行数据中的每一列:给
            for (int i = 0; i < columnCount; i++) {
                // 获取列值
                Object columnval = rs.getObject(i + 1);
                // 获取列的别名:列的别名,使用类的属性名充当
                String columnLabel = rsmd.getColumnLabel((Integer) columnval);
                // 使用反射,给对象的相应属性赋值
                Field field = clazz.getDeclaredField(columnLabel);
                field.setAccessible(true);
                field.set(t,columnval);
            }
            list.add(t);
        }
        return list;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 7.关闭资源
        JDBCUtils.closeResources(conn,ps,rs);
    }
    return null;
}



3、ResultSet与ResultSetMetaData


我们进行查询操作需要调用PreparedStatement 的 executeQuery() 方法,查询结果是一个ResultSet 对象。


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开始。

ResultSet 接口的常用方法: boolean next()、getString()

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):指示是否自动为指定列进行编号,这样这些列仍然是只读的。


如何获取 ResultSetMetaData: 调用 ResultSet 的 getMetaData() 方法即可


获取 ResultSet 中有多少列:调用 ResultSetMetaData 的 getColumnCount() 方法


获取 ResultSet 每一列的列的别名是什么:调用 ResultSetMetaData 的getColumnLabel() 方法


4、ORM思想(object relational mapping)


一个数据表对应一个java类

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

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


162a134bc8ef197d5aa37b6d2b4a45c3_image-20220909100808922.png


5、资源的释放


释放ResultSet, Statement,Connection。

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

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

6、PsUtils封装PrepareStatement操作

package com.sun.util;



import com.sun.util.JDBCUtils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/**
 * @author JumperSun
 * @date 2022-09-08-20:29
 */
public class PsUtils {
    // 通用的增、删、改操作(体现一:增、删、改 体现二:针对不同的表)
    public boolean update(String sql,Object ...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        boolean flag = false;
        try {
            // 1.获取数据库的连接
            conn = com.sun.jdbc.JDBCUtils.getConnection();
            // 2.获取PreparedStatement实例
            ps = conn.prepareStatement(sql);
            // 3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }
            // 4.执行sql语句
            flag = ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 5.关闭资源
           JDBCUtils.closeResources(conn,ps);
           return flag;
        }
    }
    // 通用的针对于不同表的查询:返回一个对象
    public <T> T getBean(Class<T> clazz,String sql,Object ...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 1.获取数据库连接
            conn = com.sun.jdbc.JDBCUtils.getConnection();
            // 2.预编译sql语句,得到PrepareStatement对象
            ps = conn.prepareStatement(sql);
            // 3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }
            // 4.执行executeQuery(),得到结果:ResultSet
            rs = ps.executeQuery();
            // 5.得到结果集的元数据:ResultSetMetaData
            ResultSetMetaData rsmd = rs.getMetaData();
            // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值
            int columnCount = rsmd.getColumnCount();
            if (rs.next()) {
                T t = clazz.newInstance();
                for (int i = 0; i < columnCount; i++) { // 遍历每一个列
                    // 获取列值
                    Object columnval = rs.getObject(i + 1);
                    // 获取列的别名:列的别名,使用类的属性名充当
                    String columnLabel = rsmd.getColumnLabel((Integer) columnval);
                    // 使用反射,给对象的相应属性赋值
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnval);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 7.关闭资源
            JDBCUtils.closeResources(conn,ps,rs);
        }
        return null;
    }
    // 通用的针对于不同表的查询:返回对象集合
    public <T> List<T> getBeanList(Class<T> clazz, String sql, Object ...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 1.获取数据库连接
            conn = com.sun.jdbc.JDBCUtils.getConnection();
            // 2.预编译sql语句,得到PrepareStatement对象
            ps = conn.prepareStatement(sql);
            // 3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }
            // 4.执行executeQuery(),得到结果:ResultSet
            rs = ps.executeQuery();
            // 5.得到结果集的元数据:ResultSetMetaData
            ResultSetMetaData rsmd = rs.getMetaData();
            // 6.通过ResultSetMetaData得到columnCount,columLable;通过ResultSet得到列值
            int columnCount = rsmd.getColumnCount();
            // 创建集合对象
            ArrayList<T> list = new ArrayList<>();
            while (rs.next()) {
                T t = clazz.newInstance();
                // 处理结果集一行数据中的每一列:给
                for (int i = 0; i < columnCount; i++) {
                    // 获取列值
                    Object columnval = rs.getObject(i + 1);
                    // 获取列的别名:列的别名,使用类的属性名充当
                    String columnLabel = rsmd.getColumnLabel((Integer) columnval);
                    // 使用反射,给对象的相应属性赋值
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnval);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 7.关闭资源
            JDBCUtils.closeResources(conn,ps,rs);
        }
        return null;
    }
}


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
14天前
|
Java 数据库连接 数据库
springboot java.lang.ClassNotFoundException: dm.jdbc.driver.DmDriver应该如何解决
通过上述步骤,可以有效解决Spring Boot项目中遇到的 `java.lang.ClassNotFoundException: dm.jdbc.driver.DmDriver`问题。确保在项目中正确添加达梦数据库的JDBC驱动依赖,并在配置文件中正确配置数据源信息,是解决此问题的关键。通过这些方法,可以确保Spring Boot项目能够正确连接达梦数据库并正常运行。
104 31
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
160 57
|
12天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
65 8
|
2月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
76 17
|
2月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
2月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
137 4
|
2月前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
84 2
|
2月前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
71 4
|
2月前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
56 5