Java JDBC 学习笔记 (2)

233 阅读6分钟

详解各个对象:

DriverManager:驱动管理对象

功能∶

1.注册驱动:告诉程序该使用哪一个数据库驱动jar包

  • static void registerDriver(Driver driver); 注册与给定的驱动程序Drivermanager。
  • Class.forName("com.mysql.jdbc.Driver" );

注意:mysql5之后的驱动jar包可以省略注册驱动的步骤,见Java JDBC 学习笔记(1) - 掘金 (juejin.cn)

通过查看源码发现: 在com.mysql.jdbc.Drivcr类中存在静态代码块

static{
    try {
        java.sql.DriverManager.registcrDriver (new Driver());
    }catch (sQLException E) {
        throw new RuntimeException( "can't register driver! ");
    }
}

2.获取数据库连接∶

  • 方法: static Connection-getConnection(string url,string user,string password)
  • 参数∶
    • url :指定连接的路径
    • 语法:jdbc :mysql://ip地址((域名):端口号/数据库名称净例子:jdbc:mysql:/ / localhost: 3306/db3
    • 细节:如果连接的是本机my sql服务器,并且mysql服务默认端口是3306,则url可以简写为: jdbc :mysql:/ l/数据库名称
    • user :用户名
    • password :密码

Connection:数据库连接对象

功能

1.获取执行sql的对象

  • Statement createsStatement()
  • PreparedStatement prepareStatement(String sql)

2.管理事务:

  • 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
  • 提交事务:commit()
  • 回滚事务:rollback()

Statement :执行sql的对象

需要要先写sql语句,像下面这样⬇⬇⬇

也就是sql最基础的语法

String sql = "create table student (id int,name varchar(20))";
  • boolean execute(string sql):可以执行任意的sql 了解
  • int executeUpdate(String sql**):**
    • DML (insert、update、delete)语句

    • DoL(create, alter、 drop)语句

      返回值∶影响的衔数,可以通过这个影响的行数判断DNL语句是否执行成功返回值>o的则执行成功,反之,则失败。(也可能是没有符合条件的)

  • ResultSet executeQuery(string sql):执行DQL (select)语句

Resultset:结果集对象,封装查询结果

  • boolean next():游标向下移动1行,判断当前行是否是最后一行末尾(是否有数据),如果是(没数据)返回false,如果不是(有数据),返回true

  • getXXX()

      XXX:代表数据类型 如:int getInt(),String getString()
      参数:
          1.代表列的编号,从1开始 如:getString(1)
          2.代表列名称。 如:getDouble("money")
         
    

其实学到这里的时候我突然就好像搞清楚了之前的比赛项目里关于xml依赖

创建一个类,为什么可以按照自己设置的对应关系把从表里提出来的数据放到类的指定的成员变量里

注意

使用步骤:

1.游标向下移动一行

2.判断是否有数据

3.获取数据(类似迭代器next(),hasNext())

while(rs.next()){
    //6.2获取数据
    int id = rs.getInt(1);
    String name = rs.getString("name");
    double money = rs.getDouble("money");

    System.out.println(id + " "  +name + " "  +money);
}

顺便一提,因为返回值是影响的行数

executeQuery(sql)

如果使用 int count = stmt.executeQuery(sql)

返回值是0

下面这段代码要注意:

  • 报错不能直接抛出
  • 因为局部变量的问题,直接在try里面创建statement的话,finally里面是不能直接用的,所以要先在开头创建好。
public class JdbcDemo7 {
    public static void main(String[] args) {
        Statement stmt = null;
        Connection conn = null;
        ResultSet rs = null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.定义sql
            String sql = "select * from account";
            //3.获取connection对象
            conn = DriverManager.getConnection("jdbc:mysql:///hush", "root", "root");
            //4.获取执行sql的对象
            stmt = conn.createStatement();
            //5.执行sql
            rs = stmt.executeQuery(sql);
            //6.处理结果
            //6.1让游标向下移动一行
            while(rs.next()){
                //6.2获取数据
                int id = rs.getInt(1);
                String name = rs.getString("name");
                double money = rs.getDouble("money");

                System.out.println(id + " "  +name + " "  +money);
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //stmt.close();
            //7.释放资源
            //避免空指针异常
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

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

2022.6.1

今天写了一段代码,查询表数据,封装为对象。然后装载集合,返回。,感觉这些功能已经完全和前文提到的之前比赛里遇到的那些完全一样了

Emp对象的属性

private int id;
private String name;
private String gender;
private double salary;
private Date join_date;
private int dept_id;

代码

public static void main(String[] args) {
    List<Emp> list = new JDBCDemo8().FindAll();
    System.out.println(list);
}
/*
查询所有emp对象
 */
public List<Emp> FindAll(){
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    List<Emp> list = null;
    try {
        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取驱动
        conn = DriverManager.getConnection("jdbc:mysql:///db4", "root", "root");
        //3.定义sql
        String sql = "select * from emp";
        //4.获取执行sql的对象
        stmt = conn.createStatement();
        //5.执行sql
        rs = stmt.executeQuery(sql);
        //6.遍历结果集,封装对象,装载集合
        Emp emp = null;//这样就是复用这个Emp对象
        list = new ArrayList<>();
        while(rs.next()){
            //获取数据
            int id = rs.getInt("id." +
                    "");
            String name = rs.getString("name");
            String gender = rs.getString("gender");
            double salary = rs.getDouble("salary");
            Date join_date = rs.getDate("join_date");
            int dept_id = rs.getInt("dept_id");

            //System.out.println(id + name + gender + salary + join_date + dept_id);
            // Emp emp = new Emp();
            //Emp..
            //这样的话,可以看到是引用了非常对次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);

            //System.out.println("emp " + emp);
            list.add(emp);
            //System.out.println("list " + list);
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }finally{
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stmt!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    return list;
}

还有,写上面代码的时候因为出现了一个小问题(return null了)所以调试了一下,然后有了下面一幕

jabc.bmp

下面画框的是获取到的信息,然而每一次获取信息都经过了上面框起来的那么多方法层层调用。(类似mapper,servicer,controller)


2022/6/14 考试暂时告一段落,这几天可以学一点,害怕,有可能会挂感觉。

写了一个Utils工具类

这里不知道为什么我用ClassLoader类加载器出了问题,报错找不到文件,然后我这里直接用src/jdbc.properties是可以的。

package 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-->统一资源定位符
//            URL res = classLoader.getResource("jdbc.properties");
//            String Path = res.getPath();
//            System.out.println(Path);
//            pro.load(new FileReader(Path));

            //2.加载文件
            pro.load(new FileReader("src/jdbc.properties"));
            //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 连接对象
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }

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

        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 释放资源
     * @param rs
     * @param stmt
     * @param conn
     */
    public static void close(ResultSet rs,Statement stmt, Connection conn){
        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();
            }
        }
    }

}

这是依赖

url=jdbc:mysql:///learning_jdbc
user=root
password=root
driver=com.mysql.jdbc.Driver

下面这是使用了工具类实现的一个用户登录功能,代码长度是不是短了很多

package Demo1;

import util.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

public class jdbcDemo9LoginIn {

    public static void main(String[] args) {
        String typeName = null;
        String typePassword = null;

        while(true){
            System.out.println("键入用户名");
            Scanner sc = new Scanner(System.in);
            typeName = sc.next();
            System.out.println("键入密码");
            typePassword = sc.next();

            if(loginIn(typeName,typePassword)){
                System.out.println("登录成功");
                break;
            }else{
                System.out.println("登陆失败");
            }

        }
    }

    public static boolean loginIn(String typeName,String typePassword){

        if(typeName == null || typePassword ==null){
            return false;
        }

        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = JDBCUtils.getConnection();

            String sql = "select * from user where name='" + typeName + "' and password='" +
            typePassword + "'";

            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);

            return rs.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.close(rs,stmt,conn);
        }
        return false;
    }
}

其实上面的代码有一个问题。

咱们来看,这里使用静态的方式将用户键入的值放到string中然后将这个字符串给sql执行

String sql = "select * from user where name='" + typeName + "' and password='" + typePassword + "'";

那么现在用户键入的值实际都变成了sql语句的一部分

那么如果这个用户输入的密码是一个恒成立的条件, 比如:a'or'a'='a

sql语句就会变成这样:

select * from user where username = (这里随便输什么都行) and password ='a'or'a'='a';

万能密码.bmp

竟然直接登录成功了!!!所以我们需要使用这个⬇⬇⬇

Preparedstatement:执行sql的对象

1.SQL注入问题:在拼接sq1时,有一些sq1的特殊关键字参与字符串的拼接。会造成安全性问题

1 输入用户随便,输入密码:a'or'a'='a

2 sql:select from user where username =‘fhdsjkf and password ='a'or'a'='a'

2.解决sql注入同题:使用preparedstatememt对象来解决

3.预编译的5QL:参数使用?作为占位符

4.步骤:

1.导入驱动jar包mysql-connector-jav5.1.37-bin-jar

2.注册驱动

3.获取数据保遥接对象cornertion

4.定义sq1

    * 注意sql的参数使用,?作为占位符如:selet * from user where username ? and password ? ;

5.获取执行sql语句的对象Preparedstatement Connection.preparestatement(String sq1)

6.给?赋值:

    * 方法:setXxx(参数1,参数2* 参数1:? 的位置编号,从1开始

        * 参数2:? 的值

7.执行sq1,接受返回结果,不需要传递sq1语句

8.处理结果

9.释放资源

解决.bmp

5.后期都会使用PreparedStatement来完成增删改查的所有操作

1.防止sql注入

2.效率更高

又要考试,惨---

话说我这进度也太太太太慢了点,考完试一定狠狠地学一把

2022/6/16