JDBC基本介绍及示例

news/2024/5/18 23:11:29 标签: JDBC, JAVA

文章目录

  • 1.JDBC基本概念
  • 2.JDBC编程步骤
    • 2.1 JDBC编程步骤总述
    • 2.2 JDBC编程详述
  • 3.JDBC实例
    • 3.1 实例一:JDBC基本使用
    • 3.2 对实例一的分析和改进
    • 3.3 实例二:Statement 和 PreparedStatement区别
  • 4.JDBC控制事务

JDBCfont_1">1.JDBC基本概念

  JDBC(Java DataBase Connectivity),即Java数据库连接,也就是用Java语言操作数据库。
  JDBC本质上是官方(Sun公司)定义的一套操作所有关系型数据库的规则,即接口(而非实现)。各个数据库厂商去实现这套接口,提供数据库驱动jar包。程序员就可以使用这些接口编程,真正执行的代码是驱动jar包中的实现类。
  综上,JDBC与数据库驱动之间为 接口和实现的关系。

JDBCfont_5">2.JDBC编程步骤

JDBCfont_6">2.1 JDBC编程步骤总述

(1)导入驱动jar包;
(2)注册驱动;
(3)获取JDBC和数据库之间的Connection对象;
(4)定义sql(尽量使用 sql参数用?作为占位符);
(5)获取执行sql语句的对象PrepareStatement;
(6)给sql中的占位符?赋值;
(7)执行sql,接收返回结果(或结果集ResultSet);
(8)处理结果;
(9)释放资源。

JDBCfont_16">2.2 JDBC编程详述

(1)导入驱动jar包
  ① 复制相应驱动jar包到项目的libs目录下;(libs目录是自己在工程中新建的目录,为了方便管理 jar包,当然也可以直接复制进项目目录中。)
  ② libs目录右键 --> 选择 Add As Library 选项。(将jar包导入到项目的工作空间中)
  在这里插入图片描述
(2)注册驱动

  • 注册驱动本质:告诉当前程序应该使用哪一个数据库驱动jar包。
  • 注册驱动方法:通过初始化驱动类com.mysql.cj.jdbc.Driver。(8.0以下jar包版本驱动类为 com.mysql.jdbc.Driver)
/**
* jar包版本为8.0及以上
* Class.forName需要捕获异常ClassNotFoundException 
*/
try{
	Class.forName("com.mysql.cj.jdbc.Driver");
	}catch (ClassNotFoundException e){
		e.printStackTrace();
	}	
/**
* jar包版本为8.0以下
* Class.forName需要捕获异常ClassNotFoundException 
*/
try{
	Class.forName("com.mysql.jdbc.Driver");
	}catch (ClassNotFoundException e){
		e.printStackTrace();
	}
  • 分析
    ① 问题:为什么可以通过类加载的方式实现注册驱动?
    ② 分析:由com.mysql.cj.jdbc.Driver类源码可知,在Driver类中定义了静态代码块,在代码块中通过registerDriver函数实现了驱动注册。而Java中静态代码块在类加载时就运行了,因此可以通过Class.forName将Driver类加载到JVM的方式,完成驱动注册。
    com.mysql.cj.jdbc.Driver类
package com.mysql.cj.jdbc;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    static {
        try {
            DriverManager.registerDriver(new Driver());//驱动注册
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

(3)获取JDBC和数据库之间的Connection对象

  • 方法
/**
url基本格式:jdbc:mysql://IP地址(域名):端口号/数据库名称
user:用户名
password:密码
*/
static Connection getConnection(String url, String user, String password);
  • 示例
try{
	Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ecargdb3?serverTimezone=UTC&useSSL=false","username","password");
	}catch (SQLException e) {
		e.printStackTrace();
    }
  • 注意:
    url除了要指定数据库的IP地址、端口号、要操作的表名外,还要设置时区serverTimeZone=UTC和useSSL=false,否则可能出现连接报错。

(4)获取执行SQL语句的对象Statement / PreparedStatement

  • Statement接口(较少使用)
    Statement中使用的sql字符串主要是拼接方式,此方式容易产生sql注入问题。在实际应用中,sql字符串主要是用?占位符的方式定义,因此主要是使用PreparedStatement接口。
//定义sql语句 
String sql = "insert into account values('wangwu',3000);
//获取Statement对象
Statement stmt = conn.createStatement();
//执行sql
stmt.executeUpdate(sql);
  • PreparedStatement接口(常用)
    使用PreparedStatement接口方式执行sql与Statement基本一致,区别主要在于定义的sql的方式上。
//定义sql语句 (username和
String sql = "insert into account values(?,?);
//获取PreparedStatement对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//给sql中的?占位符赋值
pstmt.setString(1,"wangwu");
pstmt.setDouble(2,3000);
//执行sql
pstmt.executeUpdate();

(5)给sql中?占位符赋值

  • 获取到PreparedStatement对象后,需要定义的sql语句中的占位符赋值。主要使用PreparedStatement下的方法:setXxx(参数1,参数2)
      参数1:?占位符的位置编号,从1开始。
      参数2:?占位符的值。
  • 例如,以下占位符赋值方法:
void setDouble(int parameterIndex, double x); 
void setInt(int parameterIndex, int x);
void setString(int paramterIndex, String x);
void setDate(int paramterIndex, Date x);

(6)执行sql,返回执行结果

  • 通过 Statement 接口执行sql语句方法
    boolean execute(String sql):可以执行任意sql
    int executeUpdate(String sql):可以执行DML语句(insert、update、delete),DDL语句。返回值为执行sql后的影响行数。
    ResultSet executeQuery(String sql):可以执行DQL语句(select)。返回值为结果集对象。
  • 通过 PreparedStatement 接口执行sql语句方法(与Statement基本一致,只是不用再传递sql语句参数)
    int executeUpdate();
    ResultSet executeQuery();

(7)ResultSet接口(处理结果)

  • boolean next()
    游标从当前位置下移动一行。且可以用来判断当前行是否是最后一行末尾,如果是,则返回false。如果不是,则返回true。(游标初始化在第一行之前。)
  • xxx getXxx(参数):获取数据
    ① Xxx代表数据类型。例如getInt()、getString()。
    ② 参数
      A.如果参数为int类型,则代表编号,从1开始。如getString(1):获取某行第1列的值。
      B.如果参数为String类型,则代表列的名称。如getDouble(“salary”):获取某行列名称为"balance"那列的值。

(8)释放资源
  在连接数据库到执行sql的过程中,创建的Connection、Statement/PreparedStatement、ResultSet等资源,在使用完毕后可以调用close()关闭。关闭操作要遵从从里到外的原则。(具体应用见下面实例)

JDBCfont_141">3.JDBC实例

JDBCfont_142">3.1 实例一:JDBC基本使用

  使用JDBC技术,通过java代码实现查询数据库数据并显示在java控制台上。
(1)数据库表–员工表emp
  **加粗样式**
(2)封装Emp表数据的JavaBean

package cn.ecarg.domain;

import java.util.Date;

/**
 * 封装Emp表数据的JavaBean
 */
public class Emp {
    private int id;
    private String name;
    private String gender;
    private double salary;
    private Date join_date;
    private int dept_id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Date getJoin_date() {
        return join_date;
    }

    public void setJoin_date(Date join_date) {
        this.join_date = join_date;
    }

    public int getDept_id() {
        return dept_id;
    }

    public void setDept_id(int dept_id) {
        this.dept_id = dept_id;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", salary=" + salary +
                ", join_date=" + join_date +
                ", dept_id=" + dept_id +
                '}';
    }
}

(3)定义方法,查询emp表数据将其封装为Emp对象,并装在List集合中,返回。

package cn.ecarg.jdbc;

import cn.ecarg.domain.Emp;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 定义一个方法,查询emp表的数据将其封装为Emp对象,然后装在List集合,并返回。
 */
public class JDBCDemo1 {

    public static void main(String[] args) {
        List<Emp>list = new JDBCDemo1().findAll();
        System.out.println(list);
    }

    /**
     * 查询所有emp对象
     * @return List
     */
    public List<Emp> findAll(){
        //0.导入驱动jar包
        Connection conn = null;
        Statement stmt = null;
        ResultSet res = null;
        List<Emp> list = null;
        try {
            //1.驱动注册
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.sql语句
            String sql = "select * from emp";
            //3.获取connection对象
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ecargdb2?serverTimezone=UTC&useSSL=false","root","109815");
            //4.获取执行sql对象
            stmt = conn.createStatement();
            //5.执行sql
            res = stmt.executeQuery(sql);
            //6.遍历结果集,封装对象,装载集合
            Emp emp = null;
            list = new ArrayList<Emp>();
            while(res.next()){
                //获取数据
                int id = res.getInt("id");
                String name = res.getString("name");
                String gender = res.getString("gender");
                double salary = res.getDouble("salary");
                Date join_date = res.getDate("join_date");
                int dept_id = res.getInt("dept_id");

                //创建Emp对象,并赋值
                emp = new Emp();

                emp.setId(id);
                emp.setName(name);
                emp.setGender(gender);
                emp.setSalary(salary);
                emp.setJoin_date(join_date);
                emp.setDept_id(dept_id);

                //转载集合
                list.add(emp);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if(res != null){
                try {
                    res.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
}

(4)执行结果
在这里插入图片描述

3.2 对实例一的分析和改进

(1)分析
  实例一代码中存在冗余代码。
  ① 每次都需要重复写同样的 获取JDBC和数据库之间的Connection对象 语句。
  ② 每次都需要重复写同样的 关闭资源 语句。
(2)针对于以上问题,将这些重复操作封装为一个JDBC工具类,方便调用。
  ① 为了方便连接不同数据库用户,使用配置文件,将连接数据库所用信息写入配置文件中。这样,每次连接数据库时,只需要修改配置文件信息即可。
  ② 获取 连接数据库所用信息 代码可以写在静态代码块中。这样,在类加载时就可以完成数据库信息获取,不用再写额外代码来获取。
实例文件
在这里插入图片描述
JDBCUtils.java

package cn.ecarg.util;

import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;

/**
 * JDBC工具类
 */
public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;

    /**
     * 工具类中的方法一般为静态的,方便调用。
     * 文件的读取,只需要读取一次即可拿到这些值,可以使用静态代码块
     */
    static {
        //读取资源文件,获取值
        try {
            //1.创建Properties集合类
            Properties pro = new Properties();

            //获取src路径下的文件的方式 ClassLoader类加载器
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            URL res = classLoader.getResource("jdbc.properties");
            String path = res.getPath();

            //2.加载文件
            pro.load(new FileReader(path));
            //3.获取数据,赋值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            
            //4.注册驱动
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }

    /**
     * 释放资源
     * @param conn
     * @param stmt
     */
    public static void close(Statement stmt,Connection conn){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放资源
     * @param res
     * @param conn
     * @param stmt
     */
    public static void close(ResultSet res, Statement stmt,Connection conn){
        if(res != null){
            try {
                res.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

jdbc.properties

url = jdbc:mysql://localhost:3306/ecargdb3?serverTimezone=UTC&useSSL=false
user = root
password = 109815
driver = com.mysql.cj.jdbc.Driver

JDBCDemo2.java

package cn.ecarg.jdbc;

import cn.ecarg.domain.Emp;
import cn.ecarg.util.JDBCUtils;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 定义一个方法,查询emp表的数据将其封装为对象,然后装在集合,返回。
 */
public class JDBCDemo2 {

    public static void main(String[] args) {
        List<Emp>list = new JDBCDemo2().findAll();
        System.out.println(list);
    }

    /**
     * 查询所有emp对象
     * 同时,测试JDBC工具类
     * @return
     */
    public List<Emp> findAll(){
        //0.导入驱动jar包
        Connection conn = null;
        Statement stmt = null;
        ResultSet res = null;
        List<Emp> list = null;
        try {
            //1.驱动注册
            //2.获取connection对象
            conn = JDBCUtils.getConnection();
            //3.sql语句
            String sql = "select * from emp";
            //4.获取执行sql对象
            stmt = conn.createStatement();
            //5.执行sql
            res = stmt.executeQuery(sql);
            //6.遍历结果集,封装对象,装载集合
            Emp emp = null;
            list = new ArrayList<Emp>();
            while(res.next()){
                //获取数据
                int id = res.getInt("id");
                String name = res.getString("name");
                String gender = res.getString("gender");
                double salary = res.getDouble("salary");
                Date join_date = res.getDate("join_date");
                int dept_id = res.getInt("dept_id");

                //创建Emp对象,并赋值
                emp = new Emp();

                emp.setId(id);
                emp.setName(name);
                emp.setGender(gender);
                emp.setSalary(salary);
                emp.setJoin_date(join_date);
                emp.setDept_id(dept_id);

                //转载集合
                list.add(emp);
            }
        }  catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(res,stmt,conn);
        }
        return list;
    }
}

3.3 实例二:Statement 和 PreparedStatement区别

(1)实例情景
  通过键盘录入用户名和密码,判断用户是否登陆成功。
(2)创建数据库:user表

CREATE TABLE USER(
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(32),
    PASSWORD VARCHAR(32)
);
INSERT INTO USER VALUES(NULL,'zhangsan','123');
INSERT INTO USER VALUES(NULL,'lisi','234');

在这里插入图片描述
(3)Statement接口实现

/**
* 登录方法,使用Statement实现
* @param username
* @param password
* @return 是否登录成功
*/
public boolean login(String username,String password) {
	if (username == null || password == null) {
	return false;
	}

	Connection conn = null;
	Statement stmt = null;
	ResultSet rs = null;
	//连接数据库判断是否登录成功
	try {
        //1.获取连接
        conn = JDBCUtils.getConnection();
        //2.定义sql
        String sql = "select * from user where username = '" + username + "' and password = '" + password + "'";
        //3.获取执行sql的对象
		stmt = conn.createStatement();
        //4.执行查询
        rs = stmt.executeQuery(sql);
        //5.判断
        return rs.next();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.close(rs, stmt, conn);
    }
    return false;
}

(4)PreparedStatement接口实现

/**
* 登录方法,使用PreparedStatement实现
* @param username
* @param password
* @return 是否登录成功
*/
public boolean login2(String username,String password) {
	if (username == null || password == null) {
	return false;
	}

	Connection conn = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;
	//连接数据库判断是否登录成功
	try {
        //1.获取连接
        conn = JDBCUtils.getConnection();
        //2.定义sql
        String sql = "select * from user where username = ? and password = ?";
        //3.获取执行sql的对象
		pstmt = conn.prepareStatement(sql);
		//4.给sql?赋值
        pstmt.setString(1,username);
        pstmt.setString(2,password);
        //5.执行查询时,不需要传参
        rs = pstmt.executeQuery();
        //6.判断
        return rs.next();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.close(rs, pstmt, conn);
    }
    return false;
}

(5)分析

  • 从定义的sql语句上看,当使用PreparedStatement接口时,定义sql使用?作为占位符,更加简洁清晰,不易出错。
  • 当使用Statement接口时,若定义sql语句为 sql = select * from user where username = '" + username + "' and password = '" + password + "'。当用户输入账户为XXXX;密码为 xxxxx’ or ‘a’ = 'a 时,则执行的sql语句变为:sql = select * from user where username = 'XXX' and password = ' xxxxx' or 'a' = 'a'。这样这个sql语句就变成一个恒等式,用户随便输入username和password都可以直接登录成功,这就是SQL注入问题。
  • 通过以上分析:在实际编程过程中,我们使用PreparedStatement来获取执行SQL的对象。

JDBCfont_630">4.JDBC控制事务

(1)使用Connection对象来管理事务

  • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务;
    在执行sql之前开启事务。
  • 提交事务:commit()
    当所有sql都执行完提交事务。
  • 回滚事务:rollback()
    在catch中回滚事务。

(2)示例

  • 所用数据库account
    在这里插入图片描述
  • JDBCDemo3.java
package cn.ecarg.jdbc;
import cn.ecarg.util.JDBCUtils;//本文3.2节定义的JDBC工具类
import java.sql.*;

/**
 * JDBC事务示例
 */
public class JDBCDemo3 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt1 = null;
        PreparedStatement pstmt2 = null;
        ResultSet rs = null;

        try {
            //1.获取连接(JDBCUtiles类为本文3.2节定义的JDBC工具类。)
            conn = JDBCUtils.getConnection();
            //2.开启事务
            conn.setAutoCommit(false);
            //3.定义sql
            String sql1 = "update account set balance = balance - ? where id = ?";
            String sql2 = "update account set balance = balance + ? where id = ?";
            //4.获取执行sql的对象
            pstmt1 = conn.prepareStatement(sql1);
            pstmt2 = conn.prepareStatement(sql2);
            //5.给sql?赋值
            pstmt1.setDouble(1,500);
            pstmt1.setInt(2,1);

            pstmt2.setDouble(1,500);
            pstmt2.setInt(2,2);

            //6.执行sql
            pstmt1.executeUpdate();

            //手动制造异常
            int m = 1/0;

            pstmt2.executeUpdate();

            //7.提交事务
            conn.commit();
        } catch (SQLException e) {
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            JDBCUtils.close(rs, pstmt1, conn);
            JDBCUtils.close(rs,pstmt2,null);
        }

    }
}
  • 分析
    以上示例代码中,当第一条sql语句执行成功后,因为手动制造的异常代码,第二条sql语句无法被执行。如果没有进行主动事务管理的话,会出现数据库表中一条数据被修改,另一条没有正常被修改,但仍然自动提交事务(mysql默认自动commit事务)。因此,当主动进行事务管理时,在执行完第一条sql语句后出现异常后,在异常处理中会进行回滚,恢复到sql执行之前,保证数据安全。

http://www.niftyadmin.cn/n/1741048.html

相关文章

sap模块有哪些_国内人力资源系统有哪些?

随着国内越来越多企业开始重视人力资源管理&#xff0c;国内企业的信息化管理进程在不断加快&#xff0c;人力 资源系统开始受到众多企业的青睐&#xff0c;那国内人力资源系统有哪些&#xff1f;目前市场上人力资源软件的选择有很多&#xff0c;主要的选择类型有国外知名品牌的…

命名管道提供程序无法打开与sqlserver的连接5_Linux系统编程-管道入门1

早&#xff0c;继续记录我的学习心得。当你厌倦了自己的目标时&#xff0c;怎样继续保持专注&#xff1f;误区&#xff1a;成功人士说的都是自己如何“满怀热情”去努力实现他们的目标。不管是在商业、体育还是艺术界&#xff0c;我们听到的都是“一切都归结于激情”或者“你必…

Spring jdbcTemplate的简单入门

1.基本介绍 Spring jdbcTemplate是Spring框架对JDBC的简单封装。它提供了JDBCTemplate对象&#xff0c;简化了JBDC的开发。 2.使用步骤 &#xff08;1&#xff09;导入jar包    &#xff08;2&#xff09;创建JdbcTemplate对象 JdbcTemplate template new JdbcTemplate(J…

frps server端配置_Nginx/Httpd负载均衡tomcat配置

今天我们来聊一聊用Nginx和httpd对tomcat集群做负载均衡的配置以及需要注意的点&#xff1b;在前边的演示和配置都是以单台tomcat来配置使用&#xff1b;但是在生产中单台tomcat实在支撑不了大规模的访问&#xff0c;这个时候我们就需要考虑把多台tomcat做成集群对外提供服务&a…

python学生成绩管理系统实验报告_python入门级实例:学生信息管理系统

【实例简介】简单的python实现一个管理系统 【实例截图】【核心代码】 def showInfo(): print("-"*30) print(" 学生管理系统 v1.0") print(" 1.添加学生的信息") print(" 2.删除学生的信息") print(" 3.修改学生的信息") p…

ensp删除静态路由命令_从零开始学习华为路由交换 | 配置浮动静态路由

浮动静态路由原理当网络中存在多条相同路由前缀时&#xff0c;会优先选取AD值(路由可信度&#xff0c;值越小&#xff0c;路由越优先)小的路由为主用路由&#xff0c;AD值大的路由为备份路由。当主用路由的下一跳不可达时&#xff0c;主用路由消失&#xff0c;备用路由生效切换…

python要学哪些_学习Python需要哪些基础知识?

今天是腊月二十七&#xff0c;给各位朋友拜个早年&#xff01; Python学习可以分为几个阶段&#xff0c;入门、进阶、应用。 先说说入门需要哪些基本的知识储备。 Python因为易于学习的特点&#xff0c;入门很简单&#xff0c;掌握基本的Python知识点&#xff0c;比如变量、数据…

XML基本入门

文章目录1.XML概述 1.1 基本概念 1.2 XML和HTML区别2.XML语法 2.1 XML基本语法 2.2 XML组成部分3.XML约束 3.1 约束概念 3.2 约束技术 4.xml解析 4.1 概述 4.2 解析xml的方式&#xff08;思想&#xff09; 4.3 xml常见的解析器 4.4 Jsoup解析器使用方式 4.4.1 使用步…