JDBC完成对数据库数据操作(增,删,改,查)

简介: JDBC完成对数据库数据操作(增,删,改,查)

要实现对数据库中数据的操作,首先要获取数据库的连接,关于连接,有做过详细总结:JDBC连接Mysql数据库

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


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

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

CallableStatement:用于执行 SQL 存储过程

由于Statement存在SQL注入问题,所以使用PreparedStatement实现CRUD操作


1.PreparedStatement介绍


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

PreparedStatement 对象

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

PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,

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

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


JDBCUtils工具类


public class JDBCUtils {
  /**
   * @Description 获取数据库的连接
   */
  public static Connection getConnection() throws Exception {
    // 1.读取配置文件中的4个基本信息
    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.获取连接
    Connection conn = DriverManager.getConnection(url, user, password);
    return conn;
  }
  /**
   * 
   * @Description 关闭连接和Statement的操作
   */
  public static void closeResource(Connection conn,Statement ps){
    try {
      if(ps != null)
        ps.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
    try {
      if(conn != null)
        conn.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }
  /**
   * 
   * @Description 关闭资源操作
   */
  public static void closeResource(Connection conn,Statement ps,ResultSet rs){
    try {
      if(ps != null)
        ps.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
    try {
      if(conn != null)
        conn.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
    try {
      if(rs != null)
        rs.close();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }
}


2.使用PreparedStatement实现增、删、改操作


增删改可以总结出一个通用代码:

公共部分:

数据库连接,资源的关闭。

对于变动的:

sql语句:作为参数传入;

占位符:个数,不确定,可以通过可变形参,类型为Object;占位符的个数与可变形参的个数相同

填充占位符:使用循环;


主要步骤

1.获取数据库的连接

2.预编译sql语句

3.填充占位

4.执行

5.资源的关闭


代码如下


public void update(String sql,Object ... args){
    Connection conn = null;
    PreparedStatement ps = null;
    try {
      //1.获取数据库的连接
      conn = JDBCUtils.getConnection();
      //2.获取PreparedStatement的实例 (或:预编译sql语句)
      ps = conn.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.closeResource(conn, ps);
    }
  }


3.使用PreparedStatement实现查询操作


查询操作会有结果集的返回;得专门处理结果集

一般将其封装为一个对象

针对于不同表的操作:

表也可以通过反射获取

sql语句,占位符;

查询的内容是可变的,可作为形参传入;

查询返回一个对象,返回值类型即为该对象的类型


主要步骤


1.获取数据库的连接

2.预编译sql语句,得到PreparedStatement对象

3.填充占位符

4.执行executeQuery(),得到结果集:ResultSet

5.得到结果集的元数据:ResultSetMetaData

6.通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值,使用反射,给对象的相应属性赋值

7.关闭资源


代码如下


public <T> T getInstance(Class<T> clazz, String sql, Object... args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = 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.执行executeQuery(),得到结果集:ResultSet
      rs = ps.executeQuery();
      // 5.得到结果集的元数据:ResultSetMetaData
      ResultSetMetaData rsmd = rs.getMetaData();
      // 6.1通过ResultSetMetaData得到columnCount,columnLabel;通过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(i + 1);
          // 6.2使用反射,给对象的相应属性赋值
          Field field = clazz.getDeclaredField(columnLabel);//获取类的的属性值
          field.setAccessible(true);//属性值有可能是私有的
          field.set(t, columnVal);
        }
        return t;
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 7.关闭资源
      JDBCUtils.closeResource(conn, ps, rs);
    }
    return null;
  }


细节注意:

当数据库的列名与类的属性名不一样时,可以给表的列名起别名为类的属性名

获取列的别名的方法:getColumLabel();


4.使用PreparedStatement操作BLOB类型字段


  • MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
  • 插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。

向数据表中插入大数据类型


//获取连接
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
ps.setString(1, "徐海强");
ps.setString(2, "xhq@126.com");
ps.setDate(3, new Date(new java.util.Date().getTime()));
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("xhq.png");
ps.setBlob(4, fis);
//执行
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);


修改数据表中的Blob类型字段


Connection conn = JDBCUtils.getConnection();
String sql = "update customers set photo = ? where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("coffee.png");
ps.setBlob(1, fis);
ps.setInt(2, 25);
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);


从数据表中读取大数据类型


String sql = "SELECT id, name, email, birth, photo FROM customer WHERE id = ?";
conn = getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 8);
rs = ps.executeQuery();
if(rs.next()){
  Integer id = rs.getInt(1);
    String name = rs.getString(2);
  String email = rs.getString(3);
    Date birth = rs.getDate(4);
  Customer cust = new Customer(id, name, email, birth);
    System.out.println(cust); 
    //读取Blob类型的字段
  Blob photo = rs.getBlob(5);
  InputStream is = photo.getBinaryStream();
  OutputStream os = new FileOutputStream("c.jpg");
  byte [] buffer = new byte[1024];
  int len = 0;
  while((len = is.read(buffer)) != -1){
    os.write(buffer, 0, len);
  }
    JDBCUtils.closeResource(conn, ps, rs);
  if(is != null){
    is.close();
  }
  if(os !=  null){
    os.close();
  }
}


5.批量插入


JDBC的批量处理语句包括下面三个方法:

  • addBatch(String):添加需要批量处理的SQL语句或是参数;
  • executeBatch():执行批量处理语句;
  • clearBatch():清空缓存的数据

通常我们会遇到两种批量执行SQL语句的情况:

  • 多条SQL语句的批量处理;
  • 一个SQL语句的批量传参;

高效的批量插入(案例)


举例:向数据表中插入20000条数据

数据库中提供一个goods表。创建如下:

CREATE TABLE goods(

id INT PRIMARY KEY AUTO_INCREMENT,

NAME VARCHAR(20)

);


实现层次一:使用Statement


Connection conn = JDBCUtils.getConnection();
Statement st = conn.createStatement();
for(int i = 1;i <= 20000;i++){
  String sql = "insert into goods(name) values('name_' + "+ i +")";
  st.executeUpdate(sql);
}


实现层次二:使用PreparedStatement


Connection conn = JDBCUtils.getConnection();  
String sql = "insert into goods(name)values(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for(int i = 1;i <= 20000;i++){
  ps.setString(1, "name_" + i);
  ps.executeUpdate();
}
JDBCUtils.closeResource(conn, ps);


实现层次三


/*
 * 修改1: 使用 addBatch() / executeBatch() / clearBatch()
 * 修改2:mysql服务器默认是关闭批处理的,我们需要通过一个参数,让mysql开启批处理的支持。
 *     ?rewriteBatchedStatements=true 写在配置文件的url后面
 * 修改3:使用更新的mysql 驱动:mysql-connector-java-5.1.37-bin.jar
 * 
 */
@Test
public void testInsert1() throws Exception{
  Connection conn = JDBCUtils.getConnection();
  String sql = "insert into goods(name)values(?)";
  PreparedStatement ps = conn.prepareStatement(sql);
  for(int i = 1;i <= 1000000;i++){
    ps.setString(1, "name_" + i);
    //1.“攒”sql
    ps.addBatch();
    if(i % 500 == 0){
      //2.执行
      ps.executeBatch();
      //3.清空
      ps.clearBatch();
    }
  }
  JDBCUtils.closeResource(conn, ps);
}


实现层次四


/*
* 层次四:在层次三的基础上操作
* 使用Connection 的 setAutoCommit(false)  /  commit()
*/
@Test
public void testInsert2() throws Exception{
  Connection conn = JDBCUtils.getConnection();  
  //1.设置为不自动提交数据
  conn.setAutoCommit(false);  
  String sql = "insert into goods(name)values(?)";
  PreparedStatement ps = conn.prepareStatement(sql);
  for(int i = 1;i <= 1000000;i++){
    ps.setString(1, "name_" + i); 
    //1.“攒”sql
    ps.addBatch();
    if(i % 500 == 0){
      //2.执行
      ps.executeBatch();
      //3.清空
      ps.clearBatch();
    }
  }
  //2.提交数据
  conn.commit();  
  JDBCUtils.closeResource(conn, ps);
}
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
9月前
|
SQL Java 数据库连接
除了JDBC,还有哪些常见的数据库访问技术?
除了JDBC,还有哪些常见的数据库访问技术?
738 2
|
12月前
|
存储 JSON 关系型数据库
【干货满满】解密 API 数据解析:从 JSON 到数据库存储的完整流程
本文详解电商API开发中JSON数据解析与数据库存储的全流程,涵盖数据提取、清洗、转换及优化策略,结合Python实战代码与主流数据库方案,助开发者构建高效、可靠的数据处理管道。
|
存储 缓存 数据库
数据库数据删除策略:硬删除vs软删除的最佳实践指南
在项目开发中,“删除”操作常见但方式多样,主要分为硬删除与软删除。硬删除直接从数据库移除数据,操作简单、高效,但不可恢复;适用于临时或敏感数据。软删除通过标记字段保留数据,支持恢复和审计,但增加查询复杂度与数据量;适合需追踪历史或可恢复的场景。两者各有优劣,实际开发中常结合使用以满足不同需求。
1431 4
|
11月前
|
存储 数据管理 数据库
数据字典是什么?和数据库、数据仓库有什么关系?
在数据处理中,你是否常困惑于字段含义、指标计算或数据来源?数据字典正是解答这些问题的关键工具,它清晰定义数据的名称、类型、来源、计算方式等,服务于开发者、分析师和数据管理者。本文详解数据字典的定义、组成及其与数据库、数据仓库的关系,助你夯实数据基础。
数据字典是什么?和数据库、数据仓库有什么关系?
|
10月前
|
数据采集 关系型数据库 MySQL
python爬取数据存入数据库
Python爬虫结合Scrapy与SQLAlchemy,实现高效数据采集并存入MySQL/PostgreSQL/SQLite。通过ORM映射、连接池优化与批量提交,支持百万级数据高速写入,具备良好的可扩展性与稳定性。
|
10月前
|
人工智能 Java 关系型数据库
使用数据连接池进行数据库操作
使用数据连接池进行数据库操作
237 11
|
11月前
|
存储 关系型数据库 数据库
【赵渝强老师】PostgreSQL数据库的WAL日志与数据写入的过程
PostgreSQL中的WAL(预写日志)是保证数据完整性的关键技术。在数据修改前,系统会先将日志写入WAL,确保宕机时可通过日志恢复数据。它减少了磁盘I/O,提升了性能,并支持手动切换日志文件。WAL文件默认存储在pg_wal目录下,采用16进制命名规则。此外,PostgreSQL提供pg_waldump工具解析日志内容。
1020 0
|
存储 SQL Java
数据存储使用文件还是数据库,哪个更合适?
数据库和文件系统各有优劣:数据库读写性能较低、结构 rigid,但具备计算能力和数据一致性保障;文件系统灵活易管理、读写高效,但缺乏计算能力且无法保证一致性。针对仅需高效存储与灵活管理的场景,文件系统更优,但其计算短板可通过开源工具 SPL(Structured Process Language)弥补。SPL 提供独立计算语法及高性能文件格式(如集文件、组表),支持复杂计算与多源混合查询,甚至可替代数据仓库。此外,SPL 易集成、支持热切换,大幅提升开发运维效率,是后数据库时代文件存储的理想补充方案。
|
人工智能 关系型数据库 分布式数据库
让数据与AI贴得更近,阿里云瑶池数据库系列产品焕新升级
4月9日阿里云AI势能大会上,阿里云瑶池数据库发布重磅新品及一系列产品能力升级。「推理加速服务」Tair KVCache全新上线,实现KVCache动态分层存储,显著提高内存资源利用率,为大模型推理降本提速。