Java——数据库编程JDBC之快速入门吐血总结及各关键对象详解(提供了JDBCUtils工具类)

2020-09-25 10:54:40 浏览数 (1)

1 JDBC基本概念

JDBC,Java Database Connectivity,Java数据库连接,Java语言操作数据库。JDBC的本质是SUN公司定义的一套操作所有关系型数据库的规则,即接口,各数据库厂商实现这套接口,提供数据库驱动jar包,用户可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

2 JDBC的快速入门

使用JDBC的步骤:

  • 1)导入驱动jar包:mysql-connector-java-5.1.37-bin.jar;           -- 复制jar包到项目的libs目录下;           -- 右键,Add As Library;
  • 2)编写代码,注册驱动;
  • 3)获取数据库的连接对象;
  • 4)定义sql语句;
  • 5)获取执行sql语句的对象statement;
  • 6)执行sql,接收返回结果;
  • 7)处理结果;
  • 8)释放资源,否则可能会造成内存的泄露问题。

快速入门代码如下:

代码语言:javascript复制
public class JdbcDemo1 {
    public static void main(String[] args) throws Exception {
        //1)导入驱动jar包
        //2)注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //3)获取数据库连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","root","root");
        //4)定义sql语句
        String sql = "update account set balance = 500 where id = 2";
        //5)获取执行sql的对象Statement
        Statement statement = connection.createStatement();
        //6)执行sql
        int count = statement.executeUpdate(sql);
        //7)处理结果
        System.out.println(count);
        //8)释放资源
        statement.close();
        connection.close();
    }
}

3 JDBC各对象详解

3.1 DriverManager驱动管理对象

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

static void registerDriver(Driver driver),注册与给定的驱动程序DriverManager,但是写代码时使用的是Class.forName("com.mysql.jdbc.Driver"),为什么?查看源码发现,在com.mysql.jdbc.Driver类中存在静态代码块:

【注意】:实际上mysql5以后的驱动jar包可以省略注册驱动的步骤,若没有写,程序会读取下面的文件实现自动注册,但是,实际开发中还是写上,避免有误区;

2)获取数据库连接

方法:public static java.sql.Connection getConnection(String url,  String user, String password);

参数:url:指定连接的路径,写法:jdbc:mysql://ip地址(域名):端口号/数据库名称;

           user:用户名

           password:密码

【注意】:若连接是本机的mysql服务器,且mysql默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称;

3.2 Connection数据库连接对象

1)获取执行sql的对象

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

2)管理事务

  • 开启事务:setAutoCommit(boolean autoCommit):设置参数为false,即开启事务;
  • 提交事务:commit();
  • 回滚事务:rollback();

3.3 Statement执行SQL的对象

1)执行sql

  • boolean execute(String sql):执行给定的sql语句,返回可能的结果(了解即可,不常用)。
  • int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句。 返回值:影响的行数,可以通过这个结果判断DML语句是否执行成功,返回值>0则成功,反之则失败。
  • ResultSet executeQuery(String sql):执行DQL(select)语句。

【举例】:在account表中添加一条记录、修改记录、删除一条记录,要考虑异常处理及资源释放的规范性。

代码语言:javascript复制
public class JdbcDemo2 {
    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、定义sql
            String sql = "insert into account values(null,'wangsan',2000)";
            //3、获取Connection对象
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root");
            //4、获取执行sql对象
            statement = connection.createStatement();
            //5、执行sql
            int cnt = statement.executeUpdate(sql);
            //6、处理结果
            System.out.println(cnt);
            if(cnt>0){
                System.out.println("添加成功");
            }else{
                System.out.println("添加失败");
            }

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }finally {
            //7、释放资源
            //要避免空指针异常
            if(statement!=null){
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(connection!=null){
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

以上程序为添加一条记录,修改、删除记录与其相似,只需要改下sql语句:

  • 修改:String sql = "update account set balance = 1500 where id =3";
  • 删除:String sql = "delete from account where id =3";

3.4 ResultSet结果集对象

Statement的executeQuery方法,返回的就是ResultSet结果集对象,用户需要做的就是把结果从ResultSet中取出来,涉及到两类方法:

  • next()方法:游标向下移动一行(游标默认是在表头处),判断当前行是否是最后一行之后(是否有数据),若是,则返回false,否则返回true;
  • getXxx(参数)方法:获取一列的数据;      Xxx代表数据类型,如String getString();      参数:Int,代表列的编号,从1 开始,如getString(1);                 String,代表列的名称,如getDouble("balance");

3.4.1 ResultSet的基本使用

代码语言:javascript复制
public class JdbcDemo3 {
    public static void main(String[] args) {

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、定义sql
            String sql = "select * from account";
            //3、获取Connection对象
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root");
            //4、获取执行sql对象
            statement = connection.createStatement();
            //5、执行sql
            resultSet = statement.executeQuery(sql);
            //6、处理结果
            //6.1 先让游标向下移动一行
            resultSet.next();
            int id = resultSet.getInt(1);
            String name = resultSet.getString("name");
            double balance = resultSet.getDouble(3);
            System.out.println(id  "--"  "name" "--" balance);

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }finally {
            //7、释放资源
            //要避免空指针异常
            if(resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(statement!=null){
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(connection!=null){
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

以上只是ResultSet的基本使用,前提是我们知道表中的有几条数据,但实际查询中我们不能确定查询到的结果是多少条,有存在游标超限等异常可能。

【注意使用步骤】:

  • 1)游标向下移动一行;
  • 2)判断是否有数据(仍使用next()方法);
  • 3)获取数据;

所以,将以上代码中,处理结果的语句改进如下:

代码语言:javascript复制
            //6、处理结果
            //6.1 先让游标向下移动一行
            while(resultSet.next()){
                int id = resultSet.getInt(1);
                String name = resultSet.getString("name");
                double balance = resultSet.getDouble(3);
                System.out.println(id  "--"  "name" "--" balance);
            }

3.4.2 ResultSet进阶练习(查询的数据封装为对象)

【要求】:查询emp表中的数据,将其封装为对象,然后装载集合返回。

【实现思路】:

  • 1)定义Emp类;
  • 2)定义方法public List<Emp> findAll(){}
  • 3)实现方法:select * from emp;

【源码】:其中,可以以findAll方法为模板,封装个自己的mysql的查询工具类

1)JdbcDemo.java

代码语言:javascript复制
public class JdbcDemo4 {
    public static void main(String[] args) {

        //测试
        List<Emp> list = new JdbcDemo4().findAll();
        System.out.println(list.size());
        System.out.println(list);
    }

    public List<Emp> findAll(){
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        List<Emp> list = null;
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、定义sql
            String sql = "select * from emp";
            //3、获取Connection对象
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root");
            //4、获取执行sql对象
            statement = connection.createStatement();
            //5、执行sql
            resultSet = statement.executeQuery(sql);
            //6、处理结果
            //遍历结果集,封装对象,装载集合
            Emp emp = null;
            list = new ArrayList<>();

            while(resultSet.next()){
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String gender = resultSet.getString("gender");
                double salary = resultSet.getDouble("salary");
                Date date = resultSet.getDate("join_date");
                int dept_id = resultSet.getInt("dept_id");
                emp = new Emp();
                emp.setId(id);
                emp.setName(name);
                emp.setGender(gender);
                emp.setSalary(salary);
                emp.setJoin_date(date);
                emp.setDept_id(dept_id);
                list.add(emp);
            }

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }finally {
            //7、释放资源
            //要避免空指针异常
            if(resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(statement!=null){
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if(connection!=null){
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return list;
    }
}

2)Emp.java 类

代码语言:javascript复制
public class Emp {
    private int id;
    private String name;
    private String gender;
    private Double salary;
    private Date join_date;
    private int dept_id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public Date getJoin_date() {
        return join_date;
    }

    public void setJoin_date(Date join_date) {
        this.join_date = join_date;
    }

    public int getDept_id() {
        return dept_id;
    }

    public void setDept_id(int dept_id) {
        this.dept_id = dept_id;
    }

    @Override
    public String toString() {
        return "Emp{"  
                "id="   id  
                ", name='"   name   '''  
                ", gender='"   gender   '''  
                ", salary="   salary  
                ", joindate="   join_date  
                ", dept_id="   dept_id  
                '}';
    }
}

3.4.3 抽取JdbcUtils工具类

由上一节我们可以发现,完成JDBC操作时,代码重复度特别高,每一次操作都要获取连接----释放资源,所以可以写一个工具类,来简化代码书写:

  • 抽取一个方法注册驱动;
  • 抽取一个方法获取连接对象;    需求:不传递参数,比较麻烦,还得保证工具类的通用性;    解决:通过配置文件解决此问题,提供一个文件jdbc.properties,定义url=...,user=...,password=...,读取配置文件即可获取参数,参数发生变化时只需该配置文件,无需改动代码。
  • 抽取一个方法释放资源

【JdbcUtils工具类源码】

代码语言:javascript复制
//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();
            //2、加载文件
            //动态获取src路径下的文件的方式 类加载器
            ClassLoader loader = JdbcUtils.class.getClassLoader();
            URL resource = loader.getResource("jdbc.properties");
            String path = resource.getPath();
            pro.load(new FileReader(path));
            //3、获取属性赋值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            //4、注册驱动
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }



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

    /*
    * 释放资源
    *
    * */
    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();
            }
        }
    }

    /*
     * 释放资源
     *
     * */
    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();
            }
        }
    }

}

【举例】:通过键盘录入用户名及密码,判断用户登录成功或失败

1)先准备user数据库表

代码语言:javascript复制
CREATE TABLE USER(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20),
	psd VARCHAR(20)
);

INSERT INTO USER VALUES(NULL,'zhangsan','123');
INSERT INTO USER VALUES(NULL,'lisi','456');

SELECT * FROM USER;

2)登录方法 代码及测试

代码语言:javascript复制
public class JdbcDemo6 {
    public static void main(String[] args) {
        //键盘输入
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String usr = scanner.nextLine();
        System.out.println("请输入密码:");
        String psd = scanner.nextLine();
        //登录验证
        Boolean b = login(usr,psd);
        if(b){
            System.out.println("登录OK");
        }else{
            System.out.println("登录fail");
        }

    }


    public static boolean login(String user,String psd){
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        if(user ==  null || psd == null){
            return false;
        }
        try {
            connection = JdbcUtils.getConnection();
            String sql = "select * from user where name = '" user "' and psd = '" psd "' ";
            statement = connection.createStatement();
            resultSet = statement.executeQuery(sql);
            return  resultSet.next();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.close(resultSet,statement,connection);
        }
        return false;
    }
}

3.5 PreparedStatement执行SQL的对象

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

以上登录验证,实际上没有这么干的,因为质量太差,我们试验下,输入如下用户名及密码,看下结果如何:

随便输入的用户名和密码,竟然也登录成功了。以上的问题就是SQL注入问题,看打印出的sql语句,or 'a' = 'a',这是个恒等式,所以才会出现上述的问题。

2)解决SQL注入问题:使用PreparedStatement对象

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

使用步骤:

  • 1)导入驱动jar包:mysql-connector-java-5.1.37-bin.jar;
  • 2)编写代码,注册驱动;
  • 3)获取数据库的连接对象;
  • 4)定义sql语句;       注意:sql的参数使用?作为占位符,如select * from user where user= ? and psd = ?;
  • 5)获取执行sql语句的对象PreparedStatement,Connection.prepareStatement(String sql);
  • 6)给?赋值,使用PreparedStatement中的方法,setXxx(参数1,参数2);参数1 :?位置,从1 开始,参数2:?的值;
  • 7)执行sql(不需要传递sql语句),接收返回结果;
  • 8)处理结果;
  • 9)释放资源,否则可能会造成内存的泄露问题。

【注意】:后期都会使用PrepareStatement对象完成增删改查的所有操作,可以防止SQL注入,且效率更高,不会再用Statement对象。

使用PrepareStatement完成上述的 登录验证案例如下:

代码语言:javascript复制
public static boolean login2(String user,String psd){
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        if(user ==  null || psd == null){
            return false;
        }
        try {
            connection = JdbcUtils.getConnection();
            String sql = "select * from user where name = ? and psd = ? ";
//            System.out.println(sql);
            statement = connection.prepareStatement(sql);
            //给?赋值
            statement.setString(1,user);
            statement.setString(2,psd);

            resultSet = statement.executeQuery();
            return  resultSet.next();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.close(resultSet,statement,connection);
        }
        return false;
    }

4 JDBC事务管理

关于事务可以看下这篇博客,使用JDBC控制事务,需要使用Connection对象来管理事务:

  • 开启事务:setAutoCommit(boolean autoCommit),调用方法时参数为false,在执行sql之前开启事务;
  • 提交事务:commit(),当所有sql都执行完提交事务;
  • 回滚事务:rollback(),在catch中回滚事务。

【举例】:使用JDBC进行事务管理的示例,zhangsan -500,lisi 500,注意何时开启事务,何时提交事务,何时回滚事务。

代码语言:javascript复制
public class JdbcDemo7 {

    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement pstmt1 = null;
        PreparedStatement pstmt2 = null;

        try {
            //1、获取连接
            connection = JdbcUtils.getConnection();
            connection.setAutoCommit(false);//开启事务
            //2、定义sql
            String sql1 = "update account set balance = balance - ? where id = ?";
            String sql2 = "update account set balance = balance   ? where id = ?";
            //3、获取执行sql对象
            pstmt1 = connection.prepareStatement(sql1);
            pstmt2 = connection.prepareStatement(sql2);
            //4、设置参数
            pstmt1.setDouble(1,500);
            pstmt1.setInt(2,1);
            pstmt2.setDouble(1,500);
            pstmt2.setInt(2,2);
            //5、执行sql
            pstmt1.executeUpdate();
            //手动制造异常
            int i = 3/0;
            pstmt2.executeUpdate();
            connection.commit(); //提交事务
        } catch (Exception e) {
            try {
                if(connection != null){
                    connection.rollback();//事务回滚
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.close(pstmt1,connection);
            JdbcUtils.close(pstmt2,null);
        }
    }
}

———————————————————————————————————————

本文为博主原创文章,转载请注明出处!

0 人点赞