JDBC简介

JDBC简介
一、JDBC简介
JDBC(Java DataBase Connectivity, java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 java 语言编写的类和接口组成。JDBC 提供了一个种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC 也是个商标名。

jdbc的本质:
jdbc是sun公司制定的一套接口(interface)
接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么要面向接口编程呢?
解耦合:降低程序的耦合度,提高程序的扩展力。

思考:为什么SUN制定一套JDBc接口呢?
因为每一个数据库的底层实现原理都不一样。oracle数据库有自己的原理。
MysQL数据库也有自己的原理。
Ms sqlserver数据库也有自己的原理。
每一个数据库产品都有自己独特的实现原理。

二、JDBC 的开发步骤
1)注册驱动:主要告诉 JVM 我们的程序将要使用哪一种数据库
注册驱动的方法:1.DriverManager.registerDriver(new com.mysql.jdbc.Driver());
2.利用反射机制(常用):Class.forName(“com.mysql.jdbc.Driver”);
常用原因:参数是一个字符串,字符串可以写到xxxx.properties文件中
这个方法不需要返回值,因为我们只想用他的类加载动作
2)获取连接:使用 JDBC 中的类,获得数据库的连接对象 Connection
要知道数据库URL,用户名,密码
URL:包括协议,IP地址,端口,资源名 http://39.156.66.14/index.html
就像mysql的URL一样 url=jdbc:mysql://127.0.0.1:3306/database
3)获取数据库操作对象:通过 Connection 可以获取执行者对象,Statement、PreparedStatement.
4)执行 SQL 语句:使用执行者对象,向数据库中执行 SQL 语句,然后可以得到对应的接口,有单个结果,也可能有结果集 ResultSet。
5)处理查询结果集(只有当第四步执行的是select语句的时候,才有这一步)
6)释放对象:关闭顺序:rs -> stmt 、ptmt -> conn
关闭顺序要遵循从小到大依次释放,分别对其try…catch

package com.jdbc;
import java.sql.*;
public class JDBCTest03 {
    public static void main(String[] args)  {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
       try { //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.连接数据库
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
        //3.获取数据库操作对象
        stmt = conn.createStatement();
        //4.执行SQL语句
        String sql = "select username,age,password from user.user";
        rs = stmt.executeQuery(sql);
        //5.处理查询结果集
        //Boolean flag = rs.next();
         /*  if (flag ){
               //光标指向的行有数据
               //取数据
               //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
               String username1 =rs.getNString("1");//JDBC中下标从1开始,以下代码中的123指的是第几列,用SQL查询中的字段更健壮
               String age1 = rs.getString("2");
               String pwd = rs.getString("3");
               System.out.println(username1+","+age1+","+pwd);
           }*/
         /*  while (rs.next() ){
               //光标指向的行有数据
               //取数据
               //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
               String username1 =rs.getNString("username");//以下代码中的123指的是第几列,用SQL查询中的字段更健壮
               String age1 = rs.getString("age");
               String pwd = rs.getString("password");
               System.out.println(username1+","+age1+","+pwd);
           }*/

      /**  while (rs.next() ){
            //光标指向的行有数据
            //取数据
            //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
            String username1 =rs.getNString("username");//JDBC中下标从1开始,以下代码中的123指的是第几列,用SQL查询中的字段更健壮
            String age1 = rs.getString("age");
            String pwd = rs.getString("password");
            System.out.println(username1+","+age1+","+pwd);
        }*/
            while (rs.next() ){ //next()方法的特点:如果有数据则返回true 没数据就返回false
            //光标指向的行有数据
            //取数据
            //getString的特点是:不管数据库中的数据是什么类型的,都以String类型取出
            String username1 =rs.getNString("username");//得到的类型也可以是多样的,如getInt,getDouble等
            Int age1 = rs.getInt("age");
            Double pwd = rs.getDouble("password");
            System.out.println(username1+","+age1+","+pwd);
        }
        }catch(Exception e){
           e.printStackTrace();
       }finally {
           //6.释放资源 从小到大释放 在final语句中写 确保能执行
           //1.先释放查询结果集 2. 释放数据库操作对象 3. 释放连接 
           
           if (rs !=null){
               try{
                   rs.close();
               }catch (Exception e){
                   e.printStackTrace();
               }
               if (stmt  != null){
                   try{
                       stmt.close();
                   }catch (Exception e){
                       e.printStackTrace();
                   }
               }
               if (conn  != null){
                   try{
                      conn.close();
                   }catch (Exception e){
                       e.printStackTrace();
                   }
               }
           }
       }
    }
}

解决SQL注入问题:
只要用户提供的信息部参与SQL语句的编译过程,问题就得到解决了。
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
想要用户信息部参与SQL语句的编译,那么就必须使用java.sql.PreparedStatement
PreparedStatement接口继承了java.sql.statement
PreparedStatement是属于预编译的数据库操作对象。
PreparedStatement 的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传值。
解决SQL注入的关键是:即使输入的信息中含有SQL语句中的关键字,但是这些关键字并没有参与编译,不起作用。
使用PreparedStatement防注入

package com.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JDBCTest05 {
    public static void main(String[] args) {
        //1.初始化一个界面
         Map<String,String> userInfo = initUI();
        //2.验证用户名和密码
        boolean loginSuccess = login(userInfo);
        System.out.println(loginSuccess ? "登录成功":"登录失败");
    }
    private static boolean login(Map<String, String> userInfo) {
        //设置一个标记值
        Boolean loginSuccess = false;
        //String name = userInfo.get("name");
        //String psw = userInfo.get("psw");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1.注册数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取数据库连接            conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
            // SQL语句的框子,其中一个?代表一个占位符,一个?将来会接收一个‘值’。注意占位符不能用单引号括起来,别人会被认为是一个字符串
            //3.编写SQL语句
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";
            //4.创建数据库操作对象
            ps = conn.prepareStatement(sql);
            ps.setString(1,userInfo.get("name"));//因为设置的值是String类型的,所以在什么SQL语句中会自动添加单引号
            ps.setString(2,userInfo.get("psw"));
            //5.执行SQL语句 注意要传完值之后再执行SQL语句
            rs = ps.executeQuery();
            //6.释放资源
            if (rs.next()){
               loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps!=null){
            try {
              ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
        return loginSuccess;
    }
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String  name = s.nextLine();
        System.out.println("请输入密码:");
        String psw = s.nextLine();
        Map<String,String> userInfo = new HashMap<>();
        userInfo.put("name",name);
        userInfo.put("psw",psw);
        return userInfo;
    }
}

对比Statement和PreparedStatement
1.Statement存在SQL注入问题。PreparedStatement解决了SQL注入问题
2.Statement执行效率低,编译一次执行一次 。PreparedStatement是编译一次课执行n次。因为执行SQL语句的特点,如果第二次执行相同的SQL语句,会直接执行,而Statement的语句每次都不一样,PreparedStatement的SQL语句是用占位符的,每次执行都一样。
3.PreparedStatement会在编译阶段进行安全检查。
综上所述:大多的情况下都使用PreparedStatement,只有极少数情况下用statement

5.使用statement的情况:业务要求要进行SQL注入的情况下才用。例如商品的下拉框的选项,升序或降序的情况下用Statement进行SQL注入
例如

package com.jdbc;

import java.sql.*;
import java.util.Scanner;
public class JDBCTest06desc {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入升序desc或降序asc:");
        String kw = s.nextLine();
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //1.注册连接驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","root");
            //3.获取数据库操作对象
            stmt = conn.createStatement();
            //4.执行SQL 这样写SQL会有SQL注入问题 如在输入用户名时输入abc' or '1' = 1'
            String sql = "select loginName from t_user order by loginName " + kw;
            //5.处理结果集
            rs = stmt.executeQuery(sql);
            while (rs.next())  {
                System.out.println(rs.getString("loginName"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.关闭资源
            if( rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if ( stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

三、JDBC事务
1.JDBC中的事务是自动提交的
自动提交:只要执行任意一条DML语句。则自动提交一次。这是JDBC默认的事务行为。但在实际的业务中,通常都是n条DML语句共同联合才能完成的,必须保证他们这些DML语句在通过一个事务中同时成功或同时失败。

  conn.setAutoCommit(false);//开启事务 关闭 JDBC的自动提交事务 在获取数据库连接之后开启
//在事务结束时 提交事务
  conn.commit();//提交事务
   if(conn !=null)
            try {
                conn.rollback();//事务回滚 遇到异常就回滚
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }

ACID
原子性:要么同时成功,要么同时失败

一致性:结果总数不变。就像转账,转账之前和转账之后的总数不变

持久性:一旦sql执行就不可更改

隔离性:事务间互不影响

会出现三种问题

  脏读:一个事务读到了另一个没有提交的事务

  不可重复读:在同一个事务中多次读取同一条记录,读出的结果不同

  虚读( 幻读):在一个事务中读到了别的事务中插入的数据,导致前后读出的结果不一致

四、悲观锁和乐观锁

行级锁: 在SQL语句后面加 for update 查出来的数据就锁住了,不能修改 又称为悲观锁
事务1—>读取到版本号1.1
事务2—>读取到事务1.1
其中事务1先修改了,修改之后看了版本号是1.1,于是提交修改的数据,并将版本号改成1.2
其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2,和它最初读的版本号不一致,于是就回滚。
悲观锁特点:事务必须排队执行。数据锁住了,不允许并发。
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值