JDBC

JDBC

什么是JDBC

  • Java数据库连接-Java DataBase Connectivity
  • JDBC可让Java程序员操作关系型数据库
  • JDBC基于驱动程序实现与数据库的连接和操作 (Java提供驱动接口 各大数据库厂商 根据接口做实现)

优点:

  • 统一的api 提供一致的开发过程
  • 易于学习 容易上手 代码结构稳定
  • 功能强大 执行效率高 可处理海量数据

JDBC开发流程

  1. 加载并注册JDBC驱动
  2. 创建数据库连接
  3. 创建Statement对象
  4. 遍历查询结果
  5. 关闭连接 释放资源
	Connection conn = null;
		// 使用异常捕捉 是为了防止连接后报错 一直在连接中
		try {
		// 1 加载并注册jdbc驱动  Driver 是驱动类
			Class.forName("com.mysql.cj.jdbc.Driver");
      // 2 创建数据库连接
			 conn = DriverManager.getConnection(
					// 要连接的数据库的url "mysql://localhost:3306/imooc" 代之当前要连接的数据库
					"jdbc:mysql://localhost:3306/imooc",
					"root",
					"abc123"
			);
      // 3 创建statement对象 statement对应一条或多条sql数据
      Statement statement = conn.createStatement();
			// 查询指定的sql语句 返回 resultset对象
			ResultSet resultSet = statement.executeQuery("select * from employee");
      // 4 遍历查询结果
			while (resultSet.next()) {
				Integer eno = resultSet.getInt(1);//
				String ename = resultSet.getString("ename");
				Float salary = resultSet.getFloat("salary");
				String dname = resultSet.getString("dname");
				System.out.println(dname+"-"+eno+"-"+ename+"-"+salary);
			}} catch (Exception e) {
			System.out.println("异常: " + e);
		} finally {
			// 最终都会走到这
			try {
				// 5 关闭连接 释放资源
				if (conn != null && conn.isClosed() == false) {
					// 确保非空 并且没有关闭
					conn.close();
				}
			}catch (Exception e){
				e.printStackTrace();
			}
		}
String dbDriver = "com.mysql.cj.jdbc.Driver";//记载JDBC驱动类
String dbURL = "jdbc:mysql://localhost:3306/imooc";//连接数据库字符串
String dbUsername = "root";//数据库用户名
String dbPassword = "123456";//数据库密码
// 1.加载并初始化jdbc驱动
Class.forName(dbDriver);
// 2.创建数据库连接
Connection connection = DriverManager.getConnection(dbURL,dbUsername,dbPassword);

Class.forName的作用

  • Class.forName用于加载指定的JDBC驱动类
  • Class.forName本质是通知JDBC注册这个驱动类
  • 驱动由数据库厂商自行研发,连接字符串也不同

数据库与连接字符串

数据库JDBC驱动类连接字符串
Mysql 5com.mysql.jdbc.DriverJdbc:mysql://主机ip:端口/数据库名
Mysql 8com.mysql.cj.jdbc.DriverJdbc:mysql://主机ip:端口/数据库名
Oracleoracle.jdbc.driver.OracleDriverJdbc:oracle:thin:@主机ip:端口:数据库名
SQL Servercom.mircosoft.sqlserver.jdbc.SQLServerDriverjdbc:mircosoft:sqlserver:主机ip:端口;databasename=数据库名

DriverManager (驱动管理器)

  • DriverManager用于注册/管理JDBC驱动程序
  • DriverManager.getConnection(连接字符串,用户名,密码)
  • 返回值Connection对象,对应数据库的物理网络连接

Connection对象

  • Connection是用于JDBC与数据库的网络通信对象
  • java.sql.Connection是一个接口,具体由驱动厂商实现
  • 所有数据库的操作都建立在Connection基础上

MySQL连接字符串

  • 主机ip与端口是可选设置,默认值为127.0.0.1与3306
  • 格式:jdbc:mysql:// 主机ip: 端口 /数据库名?参数列表
  • 参数列表采用url编码,格式:参数1=值1&参数2=值2

MySQL连接字符串常用参数

参数名建议参数值说明
useSSLtrue(生产)false(开发)是否禁用ssl
useUnicodeTrue启用unicode编码传输数据
characterEncodingUTF-8使用UTF-8编码传输数据
servetTimezoneAsia/Shanghai使用东8时区时间,UTC+8
allowPublicKeyRetrievalTrue允许从客户端获取公钥加密传输

JDBC异常捕捉

		try {
			Class.forName("com.mysql.cj.jdbc.Driver");//加载注册jdbc的驱动
			Connection connection = DriverManager.getConnection(                //是否禁用ssl    启用unicode编码传输数据 选择unicode的编码        服务器时区选择               允许从客户端获取公钥加密传输
					"jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
					, "root", "abc123");//创建数据库连接 使用driverMangere
			System.out.println("connection:" + connection);
		} catch (ClassNotFoundException e) {// 没有驱动依赖被加入
			System.out.println("类没有被发现异常: " + e);
		} catch (SQLException e) {
			System.out.println("其他异常: " + e);
		}

SQL注入攻击

  • SQL注入攻击指利用SQL漏洞越权获取数据的黑客行为
  • SQL注入攻击根源是未对原始SQL中的敏感字符做特殊处理

如果语句这样写 ResultSet rs = statement.executeQuery(“SELECT * FROM employee WHERE dname=’”+pdname+"’");

当输入 ’ or 1=1 or 1=’ 时 就会满足条件 将所有的用户信息泄漏

解决方案: 放弃 Statement 改用 PreparedStatement(预处理)

PreparedStatement

  • PreparedStatement预编译Statement 是Statement的子接口
  • PreparedStatement对SQL进行参数化 预防SQL注入攻击
  • PreparedStatement比Statement执行效率更高

执行的时候 会对特殊字符进行处理 例如当输入单引号时候就会被处理

    // 3.创建 PreparedStatement 对象
            String sql = "SELECT * FROM  employee WHERE dname=? AND eno > ?";// 后面是?类似于参数 多个?代表多个参数
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, pdname);//下标从1开始 将转义后的属性传入
		   preparedStatement.setInt(2,3310);
            ResultSet rs = preparedStatement.executeQuery();//此处不需要传入语句 因为已经传入过了

执行效率高是因为 不需要将之前的字符串进行拆分执行多个命令 根据?的数量进行缓存

注意事项:

  • where ?=‘abc’ // 不能将参数写在左边
  • where = ?+100 // 不能进行二次计算
  • select ? // 不能对查询字段进行参数

DbUtils工具类

  • 由于每次申请数据库链接都需要五个步骤 所以可以将其封装作为一个工具
public static Connection getConnection() throws SQLException, ClassNotFoundException {
        //1.加载并注册jdbc驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //创建dm连接
        String url = "jdbc:mysql://localhost:3306/xs?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
        String name = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url, name, password);// 此处应该设置为变量
        return conn;
    }

    public static void closeConnection(ResultSet rs, Statement statement, Connection connection) {
        try {
            if (connection != null && connection.isClosed() == false) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
    }

JDBC插入数据

 Connection conn = null;
        try {
            conn = DbUtils.getConnection();
            String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";//多个参数用?和逗号
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setInt(1, eno);
            ps.setString(2, ename);
            ps.setFloat(3, salary);
            ps.setString(4, dname);
            int cnt = ps.executeUpdate(); // 更新一下数据 此处调用 executeUpdate 对所有写操作更新
            System.out.println("cnt:" + cnt);
            System.out.println(ename + "员工入职手续已办理");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DbUtils.closeConnection(null,null,conn);
        }

JDBC更新和删除数据

 Connection conn = null;
        try {
            conn = DbUtils.getConnection();
            String sql = "UPDATE employee set salary=? WHERE eno=?";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setFloat (1,salary );
            ps.setInt(2, eno);
            int cnt = ps.executeUpdate();
            System.out.println("cnt:" + cnt);
            if (cnt == 1) {//cnt 数据不为0表示有操作
                System.out.println("员工信息已更新");
            } else {
                System.out.println("未找到");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeConnection(null, null, conn);
        }
// 删除数据
// String sql = "delete employee WHERE eno=?";
// ps.setInt(1, eno);

实现分页

 Scanner in = new Scanner(System.in);
        System.out.println("请输入页号:");
        int page = in.nextInt();
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<Employee> list = new ArrayList<>();
        try {
            conn = DbUtils.getConnection();
            String sql = "select * from employee limit ?,10";//limit 是 mysql的方言 不同的数据库特有的实现关键字叫做方言
            ps = conn.prepareStatement(sql);
            ps.setInt(1, (page - 1) * 10);
            rs = ps.executeQuery();
            while (rs.next()) {
                Integer eno = rs.getInt("eno");
                String ename = rs.getString("ename");
                Float salary = rs.getFloat("salary");
                String dname = rs.getString("dname");
                Employee emp = new Employee(eno, ename, salary, dname);
                list.add(emp);
            }
            System.out.println("list length" + list.size());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeConnection(null, null, conn);
        }

JDBC事务管理方式

什么是事务

  • 事务是以一种可靠的、一致的方式、访问和操作数据库程序单元
  • 要么把事情做完,要么什么都不做,不要做一半
  • 事务依赖于数据库实现MySQL通过事务区作为数据缓冲地带

事务的提交操作:应用数据的各种操作都会进入事务区缓存,提交了才会对数据表进行写入

事务的回滚操作:将事务区的缓存清除

JDBC允许两种事务模式

自动提交事务模式

  • 默认模式,每完成一次操作自动提交
  • 开启方式:conn.setAutoCommit(true)
  • 此模式无法保证多数据的一致性

手动提交事务模式

  • 手动提交模式是指显示调用commit()与rollback()方法管理事务
  • 手动提交开启方法:conn.setAutoCommit(false)
  • 手动提交事务可保证多数据一致性,但必须手动调用提交/回滚方法
   Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = DbUtils.getConnection();
            String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
            //JDBC默认使用自动提交模式
            conn.setAutoCommit(false);
            for (int i = 1000; i < 1500; i++) {
                if (i == 1005) {
//                    throw new RuntimeException("插入失败");
                    // 因为手动开启事务模式 所以如果没有提交数据不会更新
                }
                ps = conn.prepareStatement(sql);
                ps.setInt(1, i);
                ps.setString(2, "员工" + i);
                ps.setInt(3, i);
                ps.setString(4, "市场部");
                ps.executeUpdate();
            }
            conn.commit();
        } catch (Exception e) {// 在异常的时候 回滚操作
            System.out.println(" 异常捕获 ");
            e.printStackTrace();
            try {
                if (conn != null && !conn.isClosed()) {
                    System.out.println(" 回滚操作 ");
                    conn.rollback();
                }
            } catch (SQLException exception) {
                exception.printStackTrace();
            }
        } finally {
            DbUtils.closeConnection(null, ps, conn);
        }

JDBC中的Date日期对象的处理

//jdbc获取日期使用 java.sql.Date 其继承自java.util.Date 
// 传入的时候需要做下转换
 System.out.println("请输入入职日期:");
        String strHiredate = in.next();
// 1.将字符串 转换成 java.util.Date
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
  java.util.Date udHiredate = null;
        try {
            udHiredate = sf.parse(strHiredate);
        } catch (Exception e) {
            e.printStackTrace();
        }
    // 2.java.util.Date 转换为 java.sql.Date
        Long time = udHiredate.getTime();//获取1970年到现在的毫秒数
        java.sql.Date sdHiredate = new java.sql.Date(time);

JDBC批处理

允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。

           for (int i = 20000; i < 30000; i++) {
                ps = conn.prepareStatement(sql);
                ps.setInt(1, i);
                ps.setString(2, "员工" + i);
                ps.setInt(3, i);
                ps.setString(4, "市场部");
                // ps.executeUpdate();
                ps.addBatch();// 添加批处理
            }

Druid连接池

  • 连接池是各大厂商的独立实现
  • Druid是阿里巴巴开源连接池组件 最好的连接池之一
  • Druid对数据库连接进行有效管理与重用,最大化程序执行效率
  • 连接池负责创建管理链接,程序只负责取用与归还
// # 引入jar包
// # src下创建druid-config.properties文件输入
// # driverClassName=com.mysql.cj.jdbc.Driver
// # url=jdbc:mysql://localhost:3306/xs?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
// # username=root
// # password=root
// # initialSize=10 初始化创建几个池子的缓存 一般 初始值 跟最大设置一样 可以避免应用的时候创建增加开销
// # maxActive=20  最多创建几个池子

 //1.加载配置文件
        Properties properties = new Properties();
        //getResource获取文件路径
        String propertyFile = DruidSample.class.getResource("/druid-config.properties").getPath();
     //解码后
        try {// 获取的字符串空格会被转成%20此处需要转换回来
            propertyFile = new URLDecoder().decode(propertyFile, "UTF-8");
            properties.load(new FileInputStream(propertyFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
		Connection conn = null;
        PreparedStatement ps = null;
        //2.获取DataSource数据源对象
        try {
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            //3.创建数据库连接
            conn = dataSource.getConnection();// 获取池子中的 connnection 对象
            ps = conn.prepareStatement("select * from employee limit 0,10");

            ResultSet rs= ps.executeQuery();
            while (rs.next()) {
                Integer eno = rs.getInt("eno");
                String ename = rs.getString("ename");
                Float salary = rs.getFloat("salary");
                String dname = rs.getString("dname");
                Date hiredate = rs.getDate("hiredate");
                System.out.println("员工编号:"+eno+"员工姓名:"+ename+"工资:"+salary+"部门:"+dname+"入职日期:"+hiredate);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭占用 将连接对象放回连接池
            DbUtils.closeConnection(null,null,conn);
        }

Apache Commons DBUtils

  • commons-dbutils是Apache提供的开源 JDBC工具类库
  • 它是对JDBC的简单封装,学习成本极低
  • 使用commons-dbutils可以极大简化JDBC编码工作量
 public static void query() {
        Properties properties = new Properties();
        String propertyFile = DbUtilsSample.class.getResource("/druid-config.properties").getPath();

        try {
            //路径需要转换一下
            propertyFile = new URLDecoder().decode(propertyFile, "UTF-8");
            properties.load(new FileInputStream(propertyFile));//加载完配置了
            // 获取 DataSource数据 源对象
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            QueryRunner qr = new QueryRunner(dataSource);
            // 将返回的结果包装成一个对象
            List<Employee> list = qr.query("select * from employee limit ?,10", new BeanListHandler<>(Employee.class), new Object[]{10});
            for (Employee e : list) {
                System.out.println("name:" + e.getEname());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 没产生connection对象 不需要关闭连接
        }
    }

    public static void update() {
        Properties properties = new Properties();
        String propertyFile = DbUtilsSample.class.getResource("/druid-config.properties").getPath();
        Connection connection = null;
        try {
            //路径需要转换一下
            propertyFile = new URLDecoder().decode(propertyFile, "UTF-8");
            properties.load(new FileInputStream(propertyFile));//加载完配置了
            // 获取 DataSource数据 源对象
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            connection = dataSource.getConnection();
            connection.setAutoCommit(false);//关闭自动提交事务
            String sql1 = "update employee set salary=salary+1000 where eno=?";
            String sql2 = "update employee set salary=salary-500 where eno=?";
            QueryRunner qr = new QueryRunner();
            qr.update(connection, sql1, new Object[]{1000});
            // 不需要再次创建一个 QueryRunner 对象
            qr.update(connection, sql2, new Object[]{1001});
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (connection != null && !connection.isClosed()) {
                    connection.rollback();
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        } finally {
            try {
                if (connection != null && !connection.isClosed()) {
                    connection.close();
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值