JDBC学习笔记

此笔记基于动力节点课程

1、JDBC是什么?

Java DataBase Connectivity(Java语言连接数据库)

2、JDBC的本质是什么?

JDBC是SUN公司制定的一套接口(interface)
	java.sql.*; (这个软件包下有很多接口。)

接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么要面向接口编程?
	解耦合:降低程序的耦合度,提高程序的扩展力。
	多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
		建议:
			Animal a = new Cat();
			Animal a = new Dog();
			// 喂养的方法
			public void feed(Animal a){ // 面向父类型编程。
			
			}
		不建议:
			Dog d = new Dog();
			Cat c = new Cat();

思考:为什么SUN制定一套JDBC接口呢?
	因为每一个数据库的底层实现原理都不一样。
	Oracle数据库有自己的原理。
	MySQL数据库也有自己的原理。
	MS SqlServer数据库也有自己的原理。
	....
	每一个数据库产品都有自己独特的实现原理。

JDBC的本质到底是什么?
	一套接口。

3、JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。

classpath=.;D:\course\06-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar

以上的配置是针对于文本编辑器的方式开发,使用IDEA工具的时候,不需要配置以上的环境变量。
IDEA有自己的配置方式,将jdbc的jar包导入到项目库文件中即可。

4、JDBC编程六步

第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)

第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)

第三步:获取数据库操作对象(专门执行sql语句的对象)

第四步:执行SQL语句(DQL DML....)

第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)

第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)
package com.jdbc;

import java.sql.*;

public class TestJdbc01 {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            //1、注册驱动
            //常用类加载的方法加载驱动,因为参数是一个字符串,字符串可以写到.properties文件中
            //不需要接受返回值,因为我们只想用到他的类加载的动作
            Class.forName("com.mysql.cj.jdbc.Driver");
            //第二中加载驱动的方法
            //Driver driver = new com.mysql.cj.jdbc.Driver();//多态
            //DriverManager.registerDriver(driver);


            //2、获取连接
            String url = "jdbc:mysql://localhost/powernode";
            String user = "root";
            String password = "8888";
            connection = DriverManager.getConnection(url, user, password);
//          com.mysql.cj.jdbc.ConnectionImpl@ffaa6af

            System.out.println("数据库连接对象 = " + connection);

            //3、获取数据库操作对象
            statement = connection.createStatement();

            //4、执行sql
            String sql = "insert into dept (deptno,dname,loc)values(50,'PERSONNEL','BEIJING')";
            //executeUpdate(String sql)专门执行DML语句(INSERT,UPDATE,DELETE)
            //返回值是"影响数据库中的记录条数"
            int count = statement.executeUpdate(sql);
            System.out.println(count==1?"保存成功":"保存失败");

            sql = "delete from dept where deptno = 50";
            count = statement.executeUpdate(sql);
            System.out.println(count==1?"删除成功":"删除失败");

            sql = "update dept set loc = 'NANJING' where deptno = 40";
            count = statement.executeUpdate(sql);
            System.out.println(count==1?"更新成功":"更新失败");

            sql = "select * from dept";
            //5、处理查询结果集
            resultSet = statement.executeQuery(sql);
            while(resultSet.next()) {
                //列名称不是表中的列名称,是查询结果集中的列名称,即起别名后要用别名
                String deptno = resultSet.getString("deptno");
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");
                System.out.println(deptno+","+dname+","+loc);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            //为保证资源 一定释放,在finally语句块中关闭资源
            //从小到大依次关闭,并分别try...catch...
            try {
                if(resultSet!=null) {
                    resultSet.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(statement!=null) {
                    statement.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(connection!=null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

package com.jdbc;

import java.sql.*;
import java.util.ResourceBundle;

public class TestJdbc02 {
    public static void main(String[] args) {
        //使用资源绑定器绑定属性配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("com/cbx/jdbc/jdbc");
        String driver = bundle.getString("driver");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");
        Connection connection = null;
        Statement statement = null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url,user,password);
            statement = connection.createStatement();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(statement!=null) {
                    statement.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(connection!=null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

5、案例:用户登录业务

1、需求:
	模拟用户登录功能的实现
2、业务描述:
	程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
	用户输入用户名和密码后,提交信息,java程序收集到用户信息
	java程序连接数据库验证用户名和密码是否合法
	合法:显示登录成功
	不合法:显示登录失败
3、数据的准备:
	在实际开发过程中,表的设计会使用专业的建模工具,我们这里安装一个建模工具PowerDesigner(mac用户可以安装chiner)
package com.jdbc;

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

public class TestJdbc03 {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = InitUI();

        //验证用户名和密码
        Boolean loginSuccess = login(userLoginInfo);

        //输出结果
        System.out.println(loginSuccess?"登录成功":"登录失败");
    }

    /**
     * 验证用户名和密码
     * @param userLoginInfo 用户登录信息
     * @return true表示登录成功,false表示登录失败
     */
    private static Boolean login(Map<String, String> userLoginInfo) {
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        Boolean loginSuccess = false;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/powernode";
            String user = "root";
            String password = "8888";
            connection = DriverManager.getConnection(url,user,password);
            statement = connection.createStatement();
            String sql = "select * from t_user where loginName = '"+userLoginInfo.get("loginName")+"' and loginPassword = '"+userLoginInfo.get("loginPassword")+"'";
            resultSet = statement.executeQuery(sql);
            if(resultSet.next()) {
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(resultSet!=null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(statement!=null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码
     */
    private static Map<String, String> InitUI() {
        Scanner sc = new Scanner(System.in);

        System.out.println("用户名:");
        String loginName = sc.nextLine();

        System.out.println("密码:");
        String loginPassword = sc.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPassword",loginPassword);

        return userLoginInfo;
    }
}

在这里插入图片描述
在这里插入图片描述

6、SQL注入问题

问题说明

当上面案例输入用户名和密码时,输入fdsa和fdsa’ or ‘1’ = '1时,会显示全部信息,因为字符串拼接后会成为select * from t_user where loginName = 'fdsa' and loginPassword = 'fdsa' or '1' = '1';。这就是SQL注入问题
在这里插入图片描述

根本原因:

用户输入的信息中含有sql语句的关键字,并且这些关键字参与到sql语句的编译过程中,导致sql语句的原意被扭曲,进而达到sql注入。

解决sql注入问题:

只要用户提供的信息不参与sql语句的编译过程,问题就解决了
要想用户提供的信息不参与编译,就要使用java.sql.PreparedStatement
PreparedStatement继承了java.sql.Statement;
PreparedStatement属于预编译的数据库操作对象
PreparedStatement的原理是:预先对sql语句的框架进行编译,然后再给sql语句传“值”

package com.jdbc;
 
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class TestJdbc04 {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = InitUI();

        //验证用户名和密码
        Boolean loginSuccess = login(userLoginInfo);

        //输出结果
        System.out.println(loginSuccess?"登录成功":"登录失败");
    }

    /**
     * 验证用户名和密码
     * @param userLoginInfo 用户登录信息
     * @return true表示登录成功,false表示登录失败
     */
    private static Boolean login(Map<String, String> userLoginInfo) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        Boolean loginSuccess = false;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");

            String url = "jdbc:mysql://localhost:3306/powernode";
            String user = "root";
            String password = "8888";
            connection = DriverManager.getConnection(url,user,password);

            //sql语句框架
            //?为占位符,不能给?加引号
            String sql = "select * from t_user where loginName = ? and loginPassword = ?";

            //获取预编译的数据库操作对象
            //注意方法名,prepare后没有d
            preparedStatement = connection.prepareStatement(sql);

            //给占位符赋值,jdbc中下标从1开始
            String loginName = userLoginInfo.get("loginName");
            preparedStatement.setString(1,loginName);
            String loginPassword = userLoginInfo.get("loginPassword");
            preparedStatement.setString(2,loginPassword);

            resultSet = preparedStatement.executeQuery();
            if(resultSet.next()) {
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(resultSet!=null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码
     */
    private static Map<String, String> InitUI() {
        Scanner sc = new Scanner(System.in);

        System.out.println("用户名:");
        String loginName = sc.nextLine();

        System.out.println("密码:");
        String loginPassword = sc.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPassword",loginPassword);

        return userLoginInfo;
    }
}

在这里插入图片描述
在这里插入图片描述

Statement 和PreparedStatement对比

1、Statement未解决sql注入问题,preparedStatement解决了sql注入问题
2、Statement是编译1次执行1次,PreparedStatemnet是编译1次执行n次,所以PreparedStatement的效率会高一点(mysql中当前sql语句和上一条sql语句相同时,包括空格,不用编译,直接执行)
3、PreparedStatement会在编译阶段作类型的安全检查
4、只有当业务需求必须要支持sql注入时用Statement,如进行排序时(asc,desc),所以大部分情况下是使用PreparedStatement

7、JDBC事务机制

JDBC中的事务默认是自动提交的,但在实际的业务中,通常都是N条DML语句共同联合才能完成的,必须保证这些DML语句在同一个事务中同时成功或者同时失败

案例:银行转账

package com.jdbc;

import java.sql.*;

public class TestJdbc05 {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/powernode";
            String user = "root";
            String password = "8888";
            connection = DriverManager.getConnection(url,user,password);

            //关闭自动提交
            connection.setAutoCommit(false);

            String sql1 = "update t_account set balance = ? where accountNo = ?";
            int count = 0;

            preparedStatement = connection.prepareStatement(sql1);
            preparedStatement.setDouble(1,10000);
            preparedStatement.setInt(2,111);
            count = preparedStatement.executeUpdate();

            preparedStatement = connection.prepareStatement(sql1);
            preparedStatement.setDouble(1,10000);
            preparedStatement.setInt(2,222);
            count += preparedStatement.executeUpdate();

            if(count == 2) {
                //提交事务
                connection.commit();
                System.out.println("转账成功");
            }
        } catch (Exception e) {
            //手动回滚
            if(connection!=null) {
                try {
                    connection.rollback();
                    System.out.println("转账失败");
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            if(resultSet!=null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(preparedStatement!=null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

8、JDBC工具类的封装

package com.jdbc.utils;

import java.sql.*;

/**
 * JDBC工具类,简化JDBC编程
 */
public class DBUtil {
    //写工具类时,通常构造函数是私有的,其他方法是静态的,不需要new一个对象,直接采用类名调用
    private DBUtil() {}

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

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

    /**
     * 释放资源
     * @param connection 数据库连接对象
     * @param statement 数据库操作对象
     * @param resultSet 结果集
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        if(resultSet!=null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(statement!=null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection!=null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

9、JDBC实现模糊查询

package com.jdbc;

import com.jdbc.utils.DBUtil;

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

public class TestJdbc06 {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            String sql = "select ename from emp where ename like ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,"_A%");
            resultSet = preparedStatement.executeQuery();
            while(resultSet.next()) {
                System.out.println(resultSet.getString("ename"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,preparedStatement,resultSet);
        }
    }
}

在这里插入图片描述

在这里插入图片描述

10、悲观锁和乐观锁的概念

悲观锁:事务必须排队执行。数据锁住了,不允许并发执行。(行级锁,select后面添加for update)
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。

悲观锁机制演示

package com.jdbc;

import com.jdbc.utils.DBUtil;

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

/**
 * 这个程序开启一个事务,这个事务专门进行查询,并且使用行级锁,锁住相关的记录
 */
public class TestJdbc07 {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = DBUtil.getConnection();
            connection.setAutoCommit(false);
            String sql = "select ename,job,sal from emp where job = ? for update";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,"MANAGER");
            resultSet = preparedStatement.executeQuery();
            while(resultSet.next()) {
                System.out.println(resultSet.getString("ename")+","+resultSet.getString("job")+","+resultSet.getString("sal"));
            }
            connection.commit();//在此处加断点进行debug
        } catch (SQLException e) {
            if(connection!=null) {
                try {
                    connection.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,preparedStatement,resultSet);
        }
    }
}

package com.cbx.jdbc;

import com.cbx.jdbc.utils.DBUtil;

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

/**
 * 这个程序负责修改被锁定的记录
 */
public class TestJdbc08 {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = DBUtil.getConnection();
            connection.setAutoCommit(false);
            String sql = "update emp set sal = sal*1.1 where job = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,"MANAGER");
            int count = preparedStatement.executeUpdate();
            connection.commit();
            System.out.println(count);
        } catch (SQLException e) {
            if(connection!=null) {
                try {
                    connection.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            DBUtil.close(connection,preparedStatement,null);
        }
    }
}

在TestJdbc07的commit()处加断点,运行TestJdbc08会出现下面的报错
在这里插入图片描述

注意:

JDBC中的sql语句不用写分号
JDBC中所有下标从1开始

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿炳的旅程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值