JDBC学习笔记

JDBC:java datebase connectivity (java语言连接数据库)

本质:
Jdbc是sun公司制定的一套接口(interface)
Java.sql.* (这个软件包下有很多接口)
接口都有调用口和实现口
面向接口调用、面向接口写实现类,

为什么要面向接口编程
· 解耦性:降低程序的耦合度,提高程序的扩展性
· 多态就是典型的面向抽象编程
建议:
Animal a = new Cat();
Animal b = new Dog();
Public void feed (Animal a){ //面向父类型编程
}

不建议:
Dog a = new Dog();
Cat b = new Cat();

为什么sun要制定一个jdbc接口
因为每一个数据库的底层实现原理不一样
每一个数据库产品都有自己独特的原理
通过JDBC接口实现和每一个数据库的连接,程序员可以直接面向接口写代码
每一个数据库厂家的java程序员负责编写接口的实现类,生成一堆xxx.class文件

驱动:所有数据库驱动都是已jar包的形式存在,jar包中有很多.class 文件,这些class文件就是对JDBC接口的实现
驱动是是各大数据库厂家提供的,需要去对应官网下载

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

4:JDBC编程的六步:
第一步:注册驱动(告诉java程序,即将连接的是那个品牌的数据库)

第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,进程之间的通信)

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

第四步:执行sql语句(DQL,DML)

第五步:处理查询结果集(只有第四步执行的是select语句的时候,才有第五步查询结过集)
第六步:释放资源(使用完资源后一定要关闭,java和数据库之间属于进程的通信,开启后一定要关闭)

url:统一资源定位符(网络中某个资源的绝对路径)
包括:协议,IP,PORT,资源名
例如百度:http://182.61.200.7:80/index.html
http:// 通信协议
182.61.200.7 服务器ip地址
80 服务器软件上的端口号
Index.html 服务器上某个资源名

例如获取mysql连接
Jdbc:mysql://localhost:3306/demo
Jdbc:mysql;// mysql通信协议
Localhost 本机ip地址 127.0.0.1一样
3306 数据库端口号
Demo·· 具体数据库实例名

通信协议:通信之前就提前定好的数据传输格式

释放资源:为确保资源一定释放,在finally语句中关闭资源,并且要遵循从小到大依次关闭,分别对其try……catch

//.executeUpdate()方法是专门执行DML的语句(insert,delete,update)
//返回值是影响数据库中记录的条数

执行添加记录的sql语句
String sql = “insert into dept(deptno,dname,loc) values(50,‘人事部’,‘北京’)”;
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ?“保存成功!”:“保存失败!”);

执行删除记录的sql语句
//执行sql语句
String sql = “delete from dept where deptno = 40”;
int count = stmt.executeUpdate(sql);
System.out.println(count == 1?“删除成功~”:“删除失败!”);

执行更新记录的sql语句
String sql = “updata dept set dname = ‘销售部‘,loc = ‘天津’ where deptno = 20;
Int count = stmt.executeUpdate(sql);
Systm.out.println(count == 1?”更新成功~”:”更新失败!”);

注册驱动的常用方式
//反射机制?该方法不需要接受返回值,因为只需要类加载动作
Class.forName(“com.mysql.jdbc.Driver

配置文件:jdbc.properties,必须和代码写在同一路径中
实际开发中不建议数据库的信息写死到java程序中
使用资源绑定器绑定属性配置文件
ResourceBundle bundle = ResourceBundle.getBundle(“jdbc”);
String driver = bundle.getString(“driver”);
String url = bundle.getString(“url”);
String user = bundle.getString(“user”);
String password = bundle.getString(“password”);

处理查询结果集
/./.executeQuery()方法,专门执行DQL语句的方法,即查询
//rs为得到的结果集
String sql = “select empno,ename,sal from emp”;
Rs = stmt.executeQuery(sql);

//将光标向下移动一行,返回值为boolean类型,有下一行就为true,没有下一行为false
Rs.next();
//遍历结果集
while(rs.next()){
//数字为列的下标,从一开始,代码健壮性不高
//String empno = rs.get(“empnp”) 代码健壮性高,列名称不是表中的,而是查询//结果集中的。
String empno = rs.getString(1);
String ename = rs.getString(2);
//除了可以String类型取出,还可以以特定类型取出
double sal = rs.getDouble(3);
System.out.println(empno+""+ename+""+sal);
}

用户登录功能
/*
1:需求:模拟用户登录的功能的实现
2:业务描述:程序运行时,提供一个输入的入口,可以让用户输入用户名和密码
用户输入用户名和密码后,提交信息,java程序收集到用户信息
Java程序连接数据库验证用户名和密码是否正确
合法,登录成功
不合法,登录失败
3:数据的准备
在实际开发中表的设计会使用专门的建模工具,PowerDesigner
使用PD工具进行数据库的设计
*/

代码实现:

import java.security.spec.ECField;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JDBC_Test06 {
    public static void main(String[] args) throws SQLException {
        //初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        //验证用户名和密码
        boolean LoginSuccess = Login(userLoginInfo);

        System.out.println(LoginSuccess?"登录成功":"登录失败");
    }

    private static boolean Login(Map<String,String> userLoginInfo) throws SQLException{
        //打标记
        boolean LoginSuccess = false;
        //jdbc代码
        String LoginName = userLoginInfo.get("LoginName");
        String LoginPwd = userLoginInfo.get("LoginPwd");
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc","root","123456");
            //获取数据库操作对象
            stmt = conn.createStatement();
            //执行sql语句
            String sql = "select * from tb_qquser where qqnumber = '"+LoginName+"' and pwd = '"+LoginPwd+"'";
            rs = stmt.executeQuery(sql);
            //处理查询结果集
            if(rs.next()){
                LoginSuccess = true;
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //释放资源
            if(rs != null){
                try{
                    rs.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
            if(stmt != null){
                try{
                    stmt.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try{
                    conn.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }

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

        System.out.println("请输入用户名:");
        String LoginName = sc.nextLine();

        System.out.println("请输入密码:");
        String LoginPwd = sc.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("LoginName",LoginName);
        userLoginInfo.put("LoginPwd",LoginPwd);

        return userLoginInfo;
    }
}

SQL注入现象:
输入密码:fdsa
输入密码:fdsa’ or ‘1’=’1
登录成功

造成这种结果的原因:
密码的后一部分参与了sql语句的编译,1 = 1恒成立,
即用户输入的信息中含有sql语句的关键字,并且这些关键字参与了sql语句的编译
导致sql语句的原意被扭曲,进而达到sql注入

解决sql注入问题:
只要用户提供的信息不参与sql语句的编译过程,即使含有sql语句的关键字也没有关系
必须使用PreparedStatement,预先对sql语句框架进行编译,然后给SQL语句传值
//获取欲编译的数据库操作对象
String sql = “select * from tb_qquser where qqnumber = ? and pwd = ?”;
一个问号表示一个占位符,不能用单引号括起来
//程序会将sql语句框架给DBMS,然后DBMS进行sql语句预编译
prestmt = conn.prepareStatement(sql);
//给占位符传值(第一个?下标为1,第二个?下标为2)
prestmt.setString(1,LoginName);
prestmt.setString(2,LoginPwd);
//执行sql语句
rs = prestmt.executeQuery();

1:Stateme存在SQL注入问题,PreparedStatem能够解决此问题。
2:Stateme是编译一次执行一次,PreparedStatem是编译一次执行n次,效率方面后者更高一点。
3:PreParedStatement会在编译阶段做类型的安全检查。
综上所诉,PreparedStatement使用场景较多,只有极少数方面使用Statement,如必须支持SQL注入的sql语句,对sql语句要进行拼接。

PreparedStatement完成增删改
//获取预编译的数据库操作对象
String sql = “insert into dept(deptno,dname,loc) values(?,?,?)”;
prestmt = conn.prepareStatement(sql);
prestmt.setString(1,“60”);
prestmt.setString(2,“销售部”);
prestmt.setString(3,“上海”);

        int count = prestmt.executeUpdate();
        System.out.println(count == 1?"执行成功":"执行失败");

删除和修改同
通过setString方法将信息填充到占位符中,完成SQL语句的执行

JDBC事物机制:
Jdbc的事物是自动提交的,只要执行任意一条DML语句,则自动提交一次,默认的事物行为
但是实际的业务中,通常是n条DML语句共同联合完成的,必须保证这些DML语句在同一个事物中执行成功或者失败

案例:

//账户转账演示案例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBC_Test11 {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        PreparedStatement prestmt = null;
        try{
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc","root","123456");
            //将自动提交机制改为手动提交,开启事物
            conn.setAutoCommit(false);
            //获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ?";
            prestmt = conn.prepareStatement(sql);
            prestmt.setDouble(1,50);
            prestmt.setInt(2,101);
            int count = prestmt.executeUpdate();
            //创建异常
//            String s = null;
//            s.toString();

            prestmt.setDouble(1,50);
            prestmt.setInt(2,102);
            count += prestmt.executeUpdate();

            System.out.println(count == 2?"执行成功":"执行失败");
        // 程序能够执行到这里,表明上面的程序没有异常,事物结束,手动提交
        conn.commit();
        }catch (Exception e){
            //回滚事物
            if(conn != null){
                try{
                    conn.rollback();
                }catch (Exception e1){
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        }finally {
            if(conn != null){
                try{
                    conn.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(prestmt != null){
                try{
                    prestmt.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

重点:将自动提交单个事物改成手动提交

JDBC工具类的封装
new Package(utils)new Java Class(DBUtil)
封装代码如下

package utils;

import java.sql.*;

/*
JDBC工具类,简化JBDC编程
 */

public class DBUtil {
    /*
    工具类中的构造方法都是私有的
    因为工具类中的方法都是静态的,不用创建对象,直接类名调用
     */
    private DBUtil(){
    }
    //静态代码块在类加载时执行,并且只执行一次
    static {
        try{
            Class.forName("com.mysql.jdbc.Driver");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //获取数据库连接对象,返回连接对象
    public static Connection getConnection() throws SQLException {
        return Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo","root","123456");
    }
    /*
    关闭资源
    conn 连接对象
    ps 数据库操作对象
    rs 结果集
     */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        if(rs != null){
            try{
                rs.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        if(ps != null){
            try{
                ps.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        if(conn != null){
            try{
                conn.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

测试封装工具包
进行模糊查询

import utils.DBUtil;

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

public class JDBC_Test12 {
    public static void main(String[] args) throws SQLException {
        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");
            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"));
            }
            ps.executeQuery();
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }
    }
}

悲观锁:事物必须排队执行,数据锁住了,不允许并发(行级锁)
乐观锁:支持并发,事物不需要排队,只不过需要一个版本号

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

橘子味汽水ღ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值