程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

JDBC 详细

发布于2021-05-29 21:34     阅读(1201)     评论(0)     点赞(13)     收藏(2)


JDBC是什么?

1、Java DataBase Connectivity (Java语言连接数据库)
2、JDBC本质上是SUN公司制定的一套接口。(java.sql.*;里面有很多接口)
3、为什么要制定一套JDBC接口?
因为每一个数据库都有自己的底层原理,实现的原理也不一样。Java程序员在使用数据库时,只要实现这个的接口就可以,接口的具体实现由各大数据库厂家来实现,我们只需要去官网下载即可。
3、
在这里插入图片描述

为什么要面向接口编程? 解耦合:降低程序的耦合度,提高程序的扩展力
多态机制就是非常典型的面向抽象编程。

JDBC的编程

1.六个步骤

  1. 注册驱动 (告诉Java语句要连接的数据库时什么品牌的)
  2. 获取连接 (表示JVM的进程和数据库之间的通道打开了,这属于进程之间的通信,时重量级的)
  3. 获取数据库操作对象 (专门执行SQL语句的对象)
  4. 执行SQL语句 (DML、DQL)
  5. 处理查询结果集 (只要当第四步执行的时 select 语句时,才有第五步的处理)
  6. 释放资源 (使用完资源后一定要关闭资源。)
import java.sql.*;

public class Test01 {
    public static void main(String args[]) {
        //1、注册驱动
        Connection conn = null;
        Statement stmt = null;
        try {
            java.sql.Driver  driver = new com.mysql.cj.jdbc.Driver();//父类引用指向子类对象
            DriverManager.registerDriver(driver);
            //2、获取连接
            String url = "jdbc:mysql://127.0.0.1:3306/test";
            String user = "root";
            String password = "root";
            //com.mysql.cj.jdbc.ConnectionImpl@52af6cff
            conn = DriverManager.getConnection(url,user,password);

            System.out.println("数据库对象 = " + conn);
            //3、创建数据库操作对象
            stmt = conn.createStatement();

            //4、执行SQL语句
            String sql = "insert into dept(deptno,dname,loc) value(50,'人事部','北京')";
            //executeUpdate方法专门执行DML语句(desert,delete,update)
            //返回值是“影响数据库中的记录条数”
            int count = stmt.executeUpdate(sql);
            System.out.println(count == 1 ? "插入成功" : "插入失败");

            //5、处理查询结果集

        } catch ( SQLException e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            // 一定要在finally里面关闭
            // 一定要分别try catch
            // 从小到大关闭
            try {
                if(stmt != null) {
                    stmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

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

url:网络中某个资源的绝对路径。它由协议、IP、PORT、资源名构成
例如:http://182.61.200.7:80/index.html (百度的首页)
http:// 通信协议(通信协议就是提前定好的数据传送格式)
182.61.200.7 服务器IP地址
80 服务器上软件的端口
index.html 服务器上的某个资源名

使用java反射实现注册驱动(常用)
Class.forName()方法会导致括号里面的类加载。而com.mysql.cj.jdbc.Driver类里面的静态代码块中实现了注册驱动,而类加载会导致静态代码块执行。

import java.sql.*;

public class Test01 {
    public static void main(String args[]) {
        Connection conn = null;
        Statement stmt = null;
        try {
			//java.sql.Driver  driver = new com.mysql.cj.jdbc.Driver();
            Class.forName("com.mysql.cj.jdbc.Driver");
            //代替上面这个注释掉地代码。这样做的好处参数是字符串,而字符串可以写在配置文件中,便于修改

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

注:com.mysql.cj.jdbc.Driver实现了java.sq.Driver接口
com.mysql.cj.jdbc包里面的Driver继承了java.sql包里面的Driver

处理查询结果集

用ResultSet类的对象来保存查询结果集。然后循环遍历,判断条件使用ResultSet类的对象的next()方法。

import java.sql.*;

public class Test01 {
    public static void main(String args[]) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root");
            stmt = conn.createStatement();
            String sql = "select empno,ename,sal from emp";
            rs = stmt.executeQuery(sql);//执行DQL语句的方法
            /*
            while (rs.next()) {
                可以用下标来表示第几列(JDBC中的下标都从 1 开始)
                int empno = rs.getInt(1);
                String ename = rs.getString(2);
                Double sal = rs.getDouble(3);
                System.out.println(empno + "," + ename + "," + sal);
            }
            */
            while (rs.next()) {
                //让程序更健壮的方式是用字段名
                int empno = rs.getInt("empno");
                String ename = rs.getString("ename");
                Double sal = rs.getDouble("sal");
                System.out.println(empno + "," + ename + "," + sal);
            }

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

在这里插入图片描述
在这里插入图片描述
如果select 语句中有起别名,则遍历时必须用别名查询

import java.sql.*;

public class Test01 {
    public static void main(String args[]) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root");
            stmt = conn.createStatement();
            String sql = "select empno as a,ename,sal from emp";//注意这里给 empno 起了别名 a
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                int empno = rs.getInt("a");//因为是对结果集的遍历,所以如果字段有别名,遍历时必须用别名查询
                String ename = rs.getString("ename");
                Double sal = rs.getDouble("sal");
                System.out.println(empno + "," + ename + "," + sal);
            }

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

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

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

2.实现简单的登录(阶段小项目)

  • 实现功能:
  • 1、需求:
  •  模拟用户登录功能实现。
    
  • 2、业务描述
  •  程序运行时,提供一个输入的入口,可以让用户输入用户名和密码
    
  •  用户输入用户名和密码后,提交信息,java程序收集到用户信息
    
  •  Java程序连接数据库验证用户名和密码是否合法
    
  • 3.数据准备
  •  这里使用Navicat创建表
    
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JDBCTest02 {
    public static void main(String args[]) {
        //初始化页面
        Map<String,String> userLoginInfo = initUI();
        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        //最后输出结果
        System.out.println(loginSuccess ? "登陆成功" : "登陆失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示失败 true表示成功
     */

    private static boolean login(Map<String, String> userLoginInfo) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        boolean loginSuccess = false;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        try {
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","root");
            //创建操作数据库对象
            stmt = conn.createStatement();
            String sql = "select * from Login_tb where loginName = '"+loginName+"'and loginPwd = '"+loginPwd+"'";
            //执行SQL语句
             rs = stmt.executeQuery(sql);
            //处理查询结果集
            if(rs.next()) {
                loginSuccess = true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            try {
                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

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

            try {
                if(conn != null) {
                    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 loginName = s.nextLine();
        System.out.println("密码:");
        String loginPwd = s.nextLine();
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);
        return userLoginInfo;
    }
}

注意:上述代码中会有SQL注入问题。
SQL注入:
SQL 注入攻击是通过将恶意的SQL 查询或添加语句插入到应用的输入参数中,再在后台 SQL服务器上解析执行进行的攻击,它目前是黑客对数据库进行攻击的最常用手段之一。

例如,上述程序中,如果是如下输入:

用户名:
aaa
密码:
aaa' or '1' = '1

那么会显示登陆成功。
因为输入语句中含有SQL语句关键字,因为SQL语句在进行编译时把用户输入信息拼接到了SQL语句中,
使得:

select * from Login_tb where loginName = '"+loginName+"'and loginPwd = '"+loginPwd+"

这个SQL语句变成了:

select * from Login_tb where loginName = 'aaa'and loginPwd = 'aaa' or '1' = '1';
这样的话,where语句的判断条件就变成了
(loginName = 'aaa'and loginPwd = 'aaa')
or
('1' = '1')

'1' = '1'这个条件是一定成立的。

解决方法:
只要让输入的参数不参与SQL语句的编译即可。

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

public class JDBCTest02 {
    public static void main(String args[]) {
        //初始化页面
        Map<String,String> userLoginInfo = initUI();
        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        //最后输出结果
        System.out.println(loginSuccess ? "登陆成功" : "登陆失败");
    }



    private static boolean login(Map<String, String> userLoginInfo) {
        Connection conn = null;
        PreparedStatement ps = null;//修改 Statement 为 PreparedStatement(预编译数据库操作对象)
        ResultSet rs = null;
        boolean loginSuccess = false;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        try {

            Class.forName("com.mysql.cj.jdbc.Driver");

            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","root");

            //这里是写了一个SQL语句的框子 (SQL语句里面的 ? 是占位符,不可以用''括起来)
            String sql = "select * from Login_tb where loginName = ? and loginPwd = ? ";
            //程序执行到这里,会发送SQL语句框子给DBMS,然后DBMS进行预先编译

            //获取预编译的数据库操作对象
            ps = conn.prepareStatement(sql);//修改 createStatement() 为 prepareStatement

            //给 ? 传值。问号的下标从1开始
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);

            rs = ps.executeQuery(); //这里不传SQL语句
            if(rs.next()) {
                loginSuccess = true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {

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

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

            try {
                if(conn != null) {
                    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 loginName = s.nextLine();
        System.out.println("密码:");
        String loginPwd = s.nextLine();
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);
        return userLoginInfo;
    }
}

prepareStatement与Statement的区别:

  1. Statement存在SQL注入问题,prepareStatement不存在
  2. Statement是编译一次运行一次,而prepareStatement是编译一次运行N次,而且prepareStatement效率更高
  3. prepareStatement会在编译阶段做类型的安全检查

综上所述:prepareStatement使用较多,Statement只在极少数情况下使用,比如业务特别要求可以支持SQL注入

3.表格的增删改

使用prepareStatement实现
例如:

import java.sql.*;

public class JDBCTest03 {
    public static void main(String args[])  {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
            //写DML语句时,所插入的值统统先用 ?占位
            String sql = "insert into dept(deptno,dname,loc) value(?,?,?)";
            ps = conn.prepareStatement(sql);
            //而后再给 ?  赋值
            ps.setInt(1,60);
            ps.setString(2,"Tom");
            ps.setString(3,"Beijing");
            int count = ps.executeUpdate();
            System.out.println(count);

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

4.JDBC的实物自动提交机制

以后再补…

5.JDBC工具类的创建

import java.sql.*;

/**
 * JDBC工具类:简化JDBC编程
 */
public class DBUtil {
    /**
     * 工具类中的类都是私有的
     * 因为工具类中的方法都是静态的,不需要new对象,直接类名调用
     */

    private DBUtil() { }
    //静态代码块在类加载时执行,并且只执行一次
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
    }

    /**
     * 关闭资源
     * @param conn 连接对象
     * @param ps 数据库操作对象
     * @param rs 查询结果集对象
     */
    public static void close(Connection conn,Statement ps,ResultSet rs) {
        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();
            }
        }
    }
}

模糊查询的实现(使用PreparedStatement):

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCTest04 {
    public static void main(String args[]) {
        Connection conn = null;
        PreparedStatement ps= null;
        ResultSet rs = null;
        try {
           conn = DBUtil.getConnection();//调用工具类的方法
           String sql = "select ename from emp where ename like ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"_A%");
            rs = ps.executeQuery();
            while(rs.next()) {
                System.out.println(rs.getString("ename"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn,ps,rs);调用工具类的方法
        }
    }
}

6.乐观锁与悲观锁

悲观锁:
乐观锁:

7.最后附上前面程序用到的表

dept:
在这里插入图片描述
emp:
在这里插入图片描述
login_tb:
在这里插入图片描述



所属网站分类: 技术文章 > 博客

作者:程序员之神

链接:http://www.javaheidong.com/blog/article/207379/b4c29a75f5defbc44385/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

13 0
收藏该文
已收藏

评论内容:(最多支持255个字符)