JavaWeb--JDBC

JavaWeb--JDBC

概念

  • Java Database Connectivity (Java数据库连接)
  • Java语言操作数据库

JDBC的本质

  • JAVA官方定义的一套操作所有关系型数据库的规则,即接口,各个数据库厂商去实现这套接口提供数据库驱动jar包,我们可以使用这套接口(JDBC)编程,真正执行的代码时驱动jar包中的实现类

入门

  • 1.导入驱动jar包 mysql-connector-java-8.0.30.jar

    • 把mysql驱动jar包复制到lib目录(没有就新建命名一个)下,然后选中驱动jar包右击,选择 Add as Library...
  • 2.注册驱动

    • Class.forName("com.mysql.cj.jdbc.Driver");
      
  • 3.获取数据库的连接对象 Collection

    • Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "123456");
      
  • 4.定义sql

    • String sql = "update account set balance = 500 where id = 1";
      
  • 5.获取执行sql语句的对象 Statement

    • Statement stm = conn.createStatement();
      
  • 6.执行sql,接收返回结果

    • int count = stm.executeUpdate(sql);
      
  • 7.处理结果

    • System.out.println(count);
      
  • 8.释放资源

    • stm.close();
      conn.close();
      

JDBC--DriverManager类

功能

注册驱动

  • 告诉程序该使用哪个数据库驱动jar

  • static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager

  • 写代码使用Class.forName("com.mysql.cj.jdbc.Driver");

    • 找到jar包下Driver类的源码

    • 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!");
              }
          }
      }
      
      • 回顾静态代码块:
      • 使用static关键字修饰的代码块称为静态代码块,当类被加载的时候,静态代码块就会被执行,由于类只会加载一次,所以静态代码块只会执行一次
  • 注意:mysql5之后的驱动包可以省略注册驱动的步骤

获取数据库连接

  • static Connection getConnection(String url, String user, String password)
    试图建立一个连接到给定的数据库的网址。

    • url:指定连接的路径

      • 语法(因数据库而异,此处是mysql)

      • jdbc:mysql://ip地址(域名):端口号/数据库名称
        
      • 如果是本机的mysql服务器,并且默认的端口为3306,可以简写:jdbc:mysql:///数据库名称

    • user:用户名

    • password:密码

    • DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "123456");
      

JDBC--Connection类

获取执行sql的对象

  • Statement createStatement() -- 创建用于向数据库发送SQL语句的一 Statement对象。 
    PreparedStatement prepareStatement(String sql) -- 创建参数化的SQL语句发送到数据库的 PreparedStatement对象。 
    

管理事务

  • 开启事物:

    • void setAutoCommit(boolean autoCommit) -- 将此连接的自动提交模式设置为给定的状态。  
      
  • 提交事务

    • void commit()  -- 使自上次提交/回滚永久释放数据库的Connection对象目前持有的所有更改。 
      
  • 回滚事务

    • void rollback()  -- 撤消所有更改在当前事务并释放任何数据库锁的 Connection对象目前持有。  
      

JDBC--Statement类

执行sql的对象

  • 用于执行静态SQL语句并返回其生成的结果的对象。

  • boolean execute(String sql)  -- 执行给定的SQL语句,这可能会返回多个结果。  做了解
    
  • int executeUpdate(String sql) 
    -- 执行给定的SQL语句,这可能是DML: INSERT , UPDATE ,或 DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句。  
    
    • 此处的int返回值表示影响的行数,可以通过这个影响行数的int判断DML语句是否执行成功,返回值大于0则执行成功,反之失败
  • ResultSet executeQuery(String sql) -- 执行给定的SQL语句,该语句返回单个 ResultSet对象。 
    
    • 执行DQL(select)语句,返回一个结果集对象

练习

  • account表 添加一条insert语句

    public static void main(String[] args)  {
        Statement ste = null;
        Connection conn = null;
        try {
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //定义sql
            String sql = "insert into account values(null,'wangwu',3000)";
            //获取Collection对象
            conn = DriverManager.getConnection("jdbc:mysql:///db3","root","123456");
            //获取执行sql对象statement
            ste = conn.createStatement();
            //执行sql语句
            int count = ste.executeUpdate(sql);
            if(count>0) {
                System.out.println("添加数据成功!已影响"+count+"行数据。");
            }else{
                System.out.println("添加失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //避免空指针一场
            if(ste!=null){
                try {
                    ste.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

JDBC--RestultSet类

  • ResultSet executeQuery(String sql) -- 执行给定的SQL语句,该语句返回单个 ResultSet对象。 
    
    • 执行DQL(select)语句,返回一个结果集对象
  • 结果集对象,封装查询结果

  • next()游标向下一行

  • getxxx(参数):获取数据,xxx代表数据类型,如int getInt() getString

    • 参数
      • int:代表列的编号,从1开始 如:getString(1)
      • String:代表列的名称,如:getDouble("balance")
  • 使用步骤

    • 游标向下移动一行

    • 判断是否有数据

    • 获取数据

    • boolean next()  -- 将光标从当前位置向前移动一行。 
      

代码实现(使用封装方法完成判定循环)

public static void main(String[] args)  {
        Statement ste = null;
        Connection conn = null;
        ResultSet rs = null;//也需要关闭资源
        try {
            //注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //定义sql
            String sql = "select * from account";
            //获取Collection对象
            conn = DriverManager.getConnection("jdbc:mysql:///db3","root","123456");
            //获取执行sql对象statement
            ste = conn.createStatement();
            //使用ResultSet的语句执行sql语句
            rs = ste.executeQuery(sql);
            //处理结果(此处使用封装方法)
            printResult(rs);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //避免空指针一场
            if(ste!=null){
                try {
                    ste.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

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

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

        }

    }

    public static void printResult(ResultSet rs) throws SQLException {
        System.out.println("数据读取完成,你所查询的数据为:");
        while(rs.next()){
            //获取数据
            int id = rs.getInt(1);
            String name = rs.getString(2);
            double balance = rs.getDouble(3);
            //打印数据
            System.out.println("id:"+id+","+"姓名:"+name+","+"金额:"+balance+"元");
        }

    }

练习--select语句

  • 查询emp表的数据将其封装为对象,装载集合,返回,然后打印

    • 定义表的类
    • 定义方法
    • 实现方法
  • 代码

    public class JdbcDemo04 {
        public static void main(String[] args) {
            JdbcDemo04 jb = new JdbcDemo04();
            List<Emp> result = jb.findAll();
            //遍历
            for (Emp emp : result) {
                System.out.println(emp);
            }
        }
    
        //定义一个方法,查询emp表的数据将其封装成对象,然后装载集合,返回
        /**
         * 查询所有emp对象
         * @return
         */
        public List<Emp> findAll(){
            List<Emp> emps = new ArrayList<Emp>();
            Statement ste = null;
            Connection conn = null;
            ResultSet rs = null;
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "123456");
                ste = conn.createStatement();
                String sql = "select * from emp";
                rs = ste.executeQuery(sql);
                while(rs.next()){
                    int id = rs.getInt("id");
                    String ename = rs.getString("ename");
                    int job_id = rs.getInt("job_id");
                    int mgr = rs.getInt("mgr");
                    Date joindate = rs.getDate("joindate");
                    //这里的Date是sql包下的,而定义的成员变量是util包下的date,是他的子类,所以可以装载
                    double salary = rs.getDouble("salary");
                    double bouns = rs.getDouble("bonus");
                    int dept_id = rs.getInt("dept_id");
                    //创建emp对象
                    Emp emp = new Emp(id,ename,job_id,mgr,joindate,salary,bouns,dept_id);
                    emps.add(emp);
                }
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                if(ste!=null){
                    try {
                        ste.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
                if(conn!=null){
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
    
                if(rs!=null){
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
            return emps;
        }
    
    }
    

JDBC--工具类

  • 由于上面的练习,反复代码操作太多且繁琐,需要做一个JDBC的工具类来实现简化操作
  • 建立抽取JDBC工具类:JDBCUtils
    • 简化书写
      • 抽取一个方法注册驱动
      • 抽取一个方法获取连接对象
        • 需求:不需要传递参数,还得保证工具类的通用性
        • 解决:配置文件
      • 抽取一个方法释放资源

工具类代码

//配置文件jdbc.properties内容范例
url=jdbc:mysql:///db3
user=xxxx
password=xxxxxx
driver=com.mysql.cj.jdbc.Driver

    
    
//JDBC工具类
public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    /**
     * 文件的读取,只需要读取一次就可以拿到这些值,使用静态代码块
     */
    static {
        try {
            //创建Properties集合类
            Properties pro = new Properties();
            //获取scr路径下路径文件的方式--类加载器
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            //加载文件
            pro.load(is);
//            pro.load(new FileReader("D:\\java study\\JDBC\\src\\com\\util\\jdbc.properties")); 绝对路径,但不泛用
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


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


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

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

操作类代码

public class JdbcDemo04 {
    public static void main(String[] args) {
        JdbcDemo04 jb = new JdbcDemo04();
        List<Emp> result = jb.findAll();
        //遍历
        for (Emp emp : result) {
            System.out.println(emp);
        }
    }

    //定义一个方法,查询emp表的数据将其封装成对象,然后装载集合,返回
    /**
     * 查询所有emp对象
     * @return
     */
    //使用JDBC自建工具类简化
    public List<Emp> findAll(){
        List<Emp> emps = new ArrayList<Emp>();
        Statement ste = null;
        Connection conn = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();//此处替换
            ste = conn.createStatement();
            String sql = "select * from emp";
            rs = ste.executeQuery(sql);
            while(rs.next()){
                int id = rs.getInt("id");
                String ename = rs.getString("ename");
                int job_id = rs.getInt("job_id");
                int mgr = rs.getInt("mgr");
                Date joindate = rs.getDate("joindate");
                //这里的Date是sql包下的,而定义的成员变量是util包下的date,是他的子类,所以可以装载
                double salary = rs.getDouble("salary");
                double bouns = rs.getDouble("bonus");
                int dept_id = rs.getInt("dept_id");
                //创建emp对象
                Emp emp = new Emp(id,ename,job_id,mgr,joindate,salary,bouns,dept_id);
                emps.add(emp);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.colse(ste,conn,rs);//此处替换
        }
        return emps;
    }
}

JDBC--登陆案例

  • 需求

    • 通过键盘录入用户名和密码
    • 判断用户是否登录成功
  • 步骤

    • 创建数据库表

    • create table users(
      	id int primary key auto_increment,
      	username varchar(20),
      	password varchar(20)
      );
      
      insert into users values(null,'shijuanjun','xu12345');
      INSERT INTO users VALUES(NULL,'xuzhiyuan','xu98765');
      

创建一个类完成登录的方法

public class JdbcDemo05 {
    public static void main(String[] args) {
        String username = null;;
        String password = null;;
        Scanner sc = new Scanner(System.in);
        while(true) {
            System.out.println("请输入帐号:");
            if (sc.hasNext()) {
                username = sc.next();
            }
            System.out.println("请输入密码:");
            if (sc.hasNext()) {
                password = sc.next();
            }
            boolean b = login(username, password);
            if (b) {
                System.out.println("登陆成功");
                break;
            } else {
                System.out.println("帐号或密码错误,登录失败,重试");
                System.out.println("需要重新登录请输入1,退出系统请输入2");
                int i = sc.nextInt();
                if(i == 1){
                    continue;
                }else if(i == 2){
                    break;
                }

            }
        }
    }
    
    
    
    
    
    
    
    
    
    public static boolean login(String username, String password) {
        if(username == null || password == null){
            return false;
        }
        Statement ste = null;
        Connection conn = null;
        ResultSet rs = null;
        boolean b = false;
        try {
            conn = JDBCUtils.getConnection();//调用JDBC工具类获取配置文件连接数据库
            ste = conn.createStatement();
//            String sql = "select * from users";
            String sql = "select * from users where username = '"+username+"' and password = '"+password+"'";//改良
            rs = ste.executeQuery(sql);
//            while (rs.next()) {
//                String username1 = rs.getString("username");
//                String password1 = rs.getString("password");
//                if (username1.equals(username) && password1.equals(password)) {
//                    b = true;
//                    break;
//                }
//            }
            return rs.next();//改良
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.colse(ste, conn, rs);//JDBC工具类简化关闭
        }
        return b;
    }

JDBC--PreparedStatement类

  • SQL注入问题

    • 上面的练习随便输入用户,输入密码:a' or 'a' = 'a 会登入成功
    • SQL问题是 再拼接sql的时候,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题
  • 解决SQL注入问题:使用PreparedStatement对象来解决

  • PreparedStatement对象:表示预编译的SQL语句的对象。

  • 预编译的SQL:sql参数使用 ? 作为占位符

    • select * from users where username = ? and password = ?; (上面SQL练习的改良)

    • PreparedStatement prepareStatement(String sql) 
      创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。  
      
    • 使用connection对象调用上面方法传入sql语句

    • 给?赋值

      • 方法:setXXX(参数1,参数2)
        • 参数1,问号的位置,从1开始
        • 参数2,问号的值

修改后的登录方法代码

   public static boolean login(String username, String password) {
        if(username == null || password == null){
            return false;
        }
        PreparedStatement prste = null;
        Connection conn = null;
        ResultSet rs = null;
        boolean b = false;
        try {
            conn = JDBCUtils.getConnection();//调用JDBC工具类获取配置文件连接数据库
            String sql = "select * from users where username = ? and password = ?;";//改良
            prste = conn.prepareStatement(sql);
            prste.setString(1,username);
            prste.setString(2,password);
//            String sql = "select * from users";
            rs = prste.executeQuery();
//            while (rs.next()) {
//                String username1 = rs.getString("username");
//                String password1 = rs.getString("password");
//                if (username1.equals(username) && password1.equals(password)) {
//                    b = true;
//                    break;
//                }
//            }
            return rs.next();//改良
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.colse(prste, conn, rs);//JDBC工具类简化关闭
        }
        return b;
    }

JDBC--管理事务

概念

  • 如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败

操作

  • 开启事物:

    • void setAutoCommit(boolean autoCommit) -- 将此连接的自动提交模式设置为给定的状态。 
      
      • 在执行sql之前开启事务
  • 提交事务

    • void commit()  -- 使自上次提交/回滚永久释放数据库的Connection对象目前持有的所有更改。 
      
      • 当所有sql执行完执行提交事务
  • 回滚事务

    • void rollback()  -- 撤消所有更改在当前事务
      
      • 在异常catch中回滚事务
public class JdbcDemo06 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement prste1 = null;
        PreparedStatement prste2 = null;
        try {
            //获取连接
            conn = JDBCUtils.getConnection();
            //开启事务
            conn.setAutoCommit(false);
            //定义sql语句
            //张三 -500
            String sql1 = "update account set balance = balance - ? where name = ?";
            //李四 +500
            String sql2 = "update account set balance = balance + ? where name = ?";
            prste1 = conn.prepareStatement(sql1);
            prste2 = conn.prepareStatement(sql2);

            prste1.setDouble(1, 500);
            prste1.setString(2, "zhangsan");
            prste2.setDouble(1, 500);
            prste2.setString(2, "lisi");

            prste1.executeUpdate();
            //手动制造异常
            int ii = 3/0;
            prste2.executeUpdate();

            //提交事务
            conn.commit();

        } catch (Exception e) {//不管遇到什么异常都要回滚所以用最大异常
            e.printStackTrace();
            //出现错误在异常处回滚
            try {
                if (conn != null) {
                    conn.rollback();
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            JDBCUtils.colse(prste1, conn);
            JDBCUtils.colse(prste2, null);
        }
    }
}
posted @   Joe_ximu  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示