JDBC学习

1:JDBC是什么?

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

2:JDBC的本质是什么?

是SUN公司制定的一套接口(interface)
接口都有调用者和实现者
面向接口去调用,面向接口实现类,都属于面向接口编写

为什么要面向接口编程?
解耦合,降低程序的耦合度,提高程序的扩展力
多态机制,就是典型的面向抽象编程。(不要面向具体编程)

//建议:
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的本质

1:JDBC接口的制定
2:数据库厂商实现JDBC接口
3:程序员面向接口写代码(结合反射机制,properties文件)

import java.util.ResourceBundle;

/**
 * java程序员
 * 不需要关心具体是哪个品牌的数据库,只需要面向JDBC接口写代码
 * 面向接口编程,面向抽象编程,不面向具体编程
 * 面向接口去创建对象
 *
 */
public class JavaMy {
    public static void main(String[] args) throws Exception {
        JDBC jdbc = new MySQL();
        jdbc.getConnection();
        JDBC jdbc1 = new Oracal();
        jdbc1.getConnection();
        //上面这两种方式都是通过面向接口编程
        //也可以利用反射机制去创建对象
        ResourceBundle db = ResourceBundle.getBundle("DB");
        String classname = db.getString("classname");
        Class c = Class.forName(classname);
        JDBC jdbc2 =(JDBC)c.newInstance();
        jdbc2.getConnection();
//        new JDBC() {
//            @Override
//            public void getConnection() {
//
//            }
//        }匿名内部类  利用接口去接收对象
    }
}

4:JDBC 编程六步

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

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

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

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

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

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

jdbc完成insert

import java.sql.*;

public class Test02 {
    public static void main(String[] args) {
        // 注册驱动
        Statement statement = null;
        Connection connection = null;
        try {
            Driver driver = new com.mysql.jdbc.Driver(); // 多态,父类型的引用指向子类型的对象
            DriverManager.registerDriver(driver); // 使用 父类型的数据类型去接收子类型对象
            String url = "jdbc:mysql://127.0.0.1:3306/learn";
            String user = "root";
            String password = "333";
            connection = DriverManager.getConnection(url, user, password);
            /**
             * Creates a <code>Statement</code> object for sending
             * SQL statements to the database.
             * 获取数据库操作对象
             */
            statement = connection.createStatement();
            String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','宁波')";
            int i = statement.executeUpdate(sql);//专门执行DML语句(insert delete update)返回数据库中受影响的记录条数
            System.out.println(i == 1 ? "保存成功" : "保存失败");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //保证资源一定释放,需要在finally语句中将资源关闭,并且遵循从小到大的顺序依次关闭
            // 分别对其进行try catch
            if (statement!=null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5:利用反射注册驱动

利用反射机制让静态代码块执行,参数是字符串,可以写入配置文件
不需要接收返回值。
在这里插入图片描述

6:结合配置文件读取数据库

package com.fangjun.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;

/**
 * 将连接数据库的所有文件写入到配置文件中
 */
public class Test02 {
    public static void main(String[] args) {
        //利用资源绑定器绑定资源
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String driver1 = bundle.getString("driver");
        String url1 = bundle.getString("url");
        String user1 = bundle.getString("user");
        String pass1 = bundle.getString("pass");
        Statement statement=null;
        Connection root=null;
        try {
            Class.forName(driver1);//底层会执行对象的静态代码块
            root = DriverManager.getConnection(url1,user1,pass1);//获取数据库连接,三个参数,url,user,passwd
            statement = root.createStatement();//数据库执行的对象
            String sql="delete from dept where deptno=40";
            int i = statement.executeUpdate(sql);//这个对象去执行sql语句
            //DML语句:executeUpdate 专门执行增删改,返回int类型,受影响的条数
            //执行查询的语句:executeQuery,这个Query语句会返回一个ResultSet 
            System.out.println(i==1? "删除成功":"删除失败");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if (statement!=null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (root!=null){
                try {
                    root.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

7:处理查询结果集(重点)

package com.fangjun.jdbc;

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

public class Test05 {
    public static void main(String[] args) {
        Connection connection=null;
        Statement statement =null;
        ResultSet resultSet=null;//结果集对象
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        String driver = jdbc.getString("driver");
        String url = jdbc.getString("url");
        String user = jdbc.getString("user");
        String pass = jdbc.getString("pass");
        try {
            Class.forName(driver);
            connection=DriverManager.getConnection(url,user,pass);
            statement=connection.createStatement();
            String sql="select empno,ename,sal from emp";
            resultSet = statement.executeQuery(sql);
            while (resultSet.next()){
                String empno = resultSet.getString(1);
                String ename = resultSet.getString(2);
                String sal = resultSet.getString(3);
                System.out.println(empno+" "+ename+" "+sal);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException 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();
                }
            }
        }
    }
}

8:模拟用户登录业务

1)模拟登陆业务(存在SQL注入问题)

package com.fangjun;

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

public class Userlogin {
    public static void main(String[] args) {
        Map<String,String> userLoginInfo= initUI();
        boolean flag = login(userLoginInfo);
        System.out.println(flag? "登录成功":"登录失败");
    }

    private static boolean login(Map<String, String> userLoginInfo) {
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        String driver = jdbc.getString("driver");
        String url = jdbc.getString("url");
        String user = jdbc.getString("user");
        String pass = jdbc.getString("pass");
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;
        boolean loginflag=false;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user, pass);
            statement = connection.createStatement();
            String sql="select *from t_user where loginName='"+loginName+"' and loginPwd='"+loginPwd+"'";
            System.out.println(sql);
            resultSet = statement.executeQuery(sql);
            //只有一条
            if (resultSet.next()){
                loginflag=true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException 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 loginflag;
    }

    private static Map<String, String> initUI() {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Please input the Username");
        String Username = scanner.nextLine();
        System.out.println("Please input the passWord");
        String password = scanner.nextLine();
        HashMap<String, String> Userloginfo = new HashMap<>();
        Userloginfo.put("loginName",Username);
        Userloginfo.put("loginPwd",password);
        return Userloginfo;
    }
}

2)关于sql注入

select *from t_user where loginName='fdsa' and loginPwd='fdsa' or '1'='1'

查询结果如下
在这里插入图片描述
用户注入的信息含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,导致SQL原意被扭曲。

3)如何解决SQL注入问题
使用PreparedStatement 预先对SQL语句进行逻辑的编译,后续输入含有逻辑的SQL语句被当做普通字符串,不再参与数据库查询逻辑的操作。

package com.fangjun;

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

public class Userlogin001 {
    public static void main(String[] args) {
        Map<String,String> userLoginInfo= initUI();
        boolean flag = login(userLoginInfo);
        System.out.println(flag? "登录成功":"登录失败");
    }

    private static boolean login(Map<String, String> userLoginInfo) {
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        String driver = jdbc.getString("driver");
        String url = jdbc.getString("url");
        String user = jdbc.getString("user");
        String pass = jdbc.getString("pass");
        Connection connection=null;
        PreparedStatement statement=null; //这里修改成 PreparedStatement
        ResultSet resultSet=null;
        boolean loginflag=false;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user, pass);
            //sql语句的模板,?表示一个占位符;注意:占位符不要使用单引号
            String sql="select *from t_user where loginName = ? and loginPwd = ?";
            //执行到此处,会发送SQL模板给DBMS,预先对SQL语句的逻辑进行编译,之后不在会添加其他逻辑,逻辑符号位普通字符
            statement=connection.prepareStatement(sql);
            //此处给站位符号进行传值操作,第一个问号下标为1
            statement.setString(1,loginName);
            statement.setString(2,loginPwd);
            resultSet = statement.executeQuery(); //依旧是需要执行,需要返回结果集
            //只有一条
            if (resultSet.next()){
                loginflag=true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException 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 loginflag;
    }

    private static Map<String, String> initUI() {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Please input the Username");
        String Username = scanner.nextLine();
        System.out.println("Please input the passWord");
        String password = scanner.nextLine();
        HashMap<String, String> Userloginfo = new HashMap<>();
        Userloginfo.put("loginName",Username);
        Userloginfo.put("loginPwd",password);
        return Userloginfo;
    }
}

4)对比Statement 与 PreparedStatement
a:前者存在SQL注入问题,后者解决此问题
b:PreparedStatement 预先编译含有占位符的SQL语句,后续只需要简单传值,只编译一次,效率较高一些。
c:PreparedStatement 在编译期间会自动进行类型的安全检查。
注意:Statement 支持SQL语句的拼接,凡是业务需要进行SQL语句拼接的,必须使用Statement ,传值使用PreparedStatement

9:jdbc事务机制

jdbc事务自动提交,只要执行任意一条DML语句,则自动提交一次;这是JDBC默认的事务行为。
结论:JDBC中只要执行任意一条DML语句就提交一次。
代码验证:

package com.fangjun;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ResourceBundle;

public class JdbcTest001 {
    public static void main(String[] args) {
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        String driver = jdbc.getString("driver");
        String url = jdbc.getString("url");
        String user = jdbc.getString("user");
        String pass = jdbc.getString("pass");
        Connection connection=null;
        PreparedStatement statement=null; //这里修改成 PreparedStatement
        ResultSet resultSet=null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url,user,pass);
            statement = connection.prepareStatement("update dept set dname=? where deptno=?");
            statement.setString(1,"x");
            statement.setString(2,"30");
            int count = statement.executeUpdate();
            System.out.println(count); //执行到这里数据库已经被修改

            statement.setString(1,"y");
            statement.setString(2,"20");
            count = statement.executeUpdate();

            System.out.println(count);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


(补充 代码演示银行账户转账  总金额会产生错误)

package com.fangjun;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ResourceBundle;

public class JdbcTest001 {
public static void main(String[] args) {
ResourceBundle jdbc = ResourceBundle.getBundle(“jdbc”);
String driver = jdbc.getString(“driver”);
String url = jdbc.getString(“url”);
String user = jdbc.getString(“user”);
String pass = jdbc.getString(“pass”);
Connection connection=null;
PreparedStatement statement=null; //这里修改成 PreparedStatement
ResultSet resultSet=null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url,user,pass);
statement = connection.prepareStatement(“update t_act set balance=? where actno=?”);
statement.setDouble(1,10000);
statement.setInt(2,111);
int count = statement.executeUpdate();
System.out.println(count); //执行到这里数据库已经被修改

        String s=null;
        s.toString();

        statement.setDouble(1,10000);
        statement.setInt(2,222);
        count += statement.executeUpdate();


        System.out.println(count==2? "right":"error");

    } catch (Exception e) {
        e.printStackTrace();
    }

}

}

将JDBC的事务修改成手动提交

package com.fangjun;

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

public class JdbcTest001 {
    public static void main(String[] args) {
        ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
        String driver = jdbc.getString("driver");
        String url = jdbc.getString("url");
        String user = jdbc.getString("user");
        String pass = jdbc.getString("pass");
        Connection connection=null;
        PreparedStatement statement=null; //这里修改成 PreparedStatement
        ResultSet resultSet=null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url,user,pass);
            connection.setAutoCommit(false); //将jdbc的自动提交机制进行关闭
            statement = connection.prepareStatement("update t_act set balance=? where actno=?");
            statement.setDouble(1,10000);
            statement.setInt(2,111);
            int count = statement.executeUpdate();
            System.out.println(count); //执行到这里数据库已经被修改

            String s=null;
            s.toString();

            statement.setDouble(1,10000);
            statement.setInt(2,222);
            count += statement.executeUpdate();


            System.out.println(count==2? "right":"error");
            //程序执行到这里如果没有发生意外,可以提交所有事务.手动提交数据
            connection.commit();
        } catch (Exception e) {
            //如果出现意外,在这里回滚事务,确保数据的安全
            if (connection!=null){
                try {
                    connection.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        }

    }
}

10:JDBC工具类的封装

package com.fangjun;

import java.sql.*;


public class DbTools {

    //工具类构造方法建议私有化
    private DbTools() {
    }

    //将注册驱动的行为执行一次,在类加载的时候执行
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象,异常抛出,在主程序中进行捕捉
     * @return
     * @throws SQLException
     */
    public static Connection getCollection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/learn","root","333");
    }

    /**
     * 关闭资源
     * @param resultSet
     * @param statement 面向接口编程,Statement是父接口
     * @param connection
     */

    public static void close(ResultSet resultSet, Statement statement,Connection connection){
        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();
            }
        }
    }

}

JDBC借助工具类实现模糊查询

import com.fangjun.DbTools;

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

public class MohuQuery {
    public static void main(String[] args) {
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        try {
            connection = DbTools.getCollection();
            preparedStatement = connection.prepareStatement("select ename from emp where ename like ?");
            preparedStatement.setString(1,"_A%");//查询第二个字母为A
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                String ename = resultSet.getString("ename");
                System.out.println(ename);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            DbTools.close(resultSet,preparedStatement,connection);
        }
    }
}

11:悲观锁与乐观锁

for update 悲观锁 锁住一行记录(行级锁)
在这里插入图片描述
jdbc代码演示行级锁
加行级锁

package com.fangjun;

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

/**
 * 代码演示行级锁,这个进行查询,使用行级锁
 */
public class hangjisuo {
    public static void main(String[] args) {
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        try {
            connection = DbTools.getCollection();
            connection.setAutoCommit(false);//关闭jdbc的自动提交机制
            preparedStatement = connection.prepareStatement("select ename,job,sal from emp where job=? for update");
            preparedStatement.setString(1,"MANAGER");
            resultSet=preparedStatement.executeQuery();
            while (resultSet.next()){
                System.out.println(resultSet.getString("ename")+","+resultSet.getString("job")+","+resultSet.getDouble("sal"));
            }
            connection.commit();//开启jdbc的手动提交机制
        } catch (Exception e) {
            if (connection!=null){ //avoid nullpointexception
                try {
                    connection.rollback();//遇到错误的自动回滚事务机制
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        }finally {
            DbTools.close(resultSet,preparedStatement,connection);
        }
    }
}

另外的代码对锁定数据执行修改操作

package com.fangjun;

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

/**
 * 操作被锁定的数据
 */
public class hangjisuo01 {
    public static void main(String[] args) {
        Connection connection=null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        try {
            connection = DbTools.getCollection();
            connection.setAutoCommit(false);//关闭自动提交机制
            preparedStatement=connection.prepareStatement("update emp set sal=sal*1.1 where job=?");
            preparedStatement.setString(1,"MANAGER");
            int count = preparedStatement.executeUpdate();
            System.out.println(count);
            connection.commit();//手动提交机制
        } catch (Exception e) {
            if (connection!=null){
                try {
                    connection.close();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        }finally {
            DbTools.close(null,preparedStatement,connection);
        }

    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值