JDBC

1>JDBC是什么

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

2>JDBC的本质什么

JDBC是SUN公司制定的一套接口(interface)
java.sql.*这个软件包下的。
接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程

2.1、为什么SUN要制定一套JDBC接口

因为每一个数据库的底层实现原理都不一样。
Oracle数据库有自己的原理。
MySQL数据也有自己的原理。
MS SqlServer也有自己的原理。   

在这里插入图片描述

  • 作为Java程序员不需要针对于每一种数据库进行特定编程,只需要面向接口编程,面向抽象编程,不需要面向具体编程,提高了程序的扩展力

2.2、为什么要面向接口编程

解耦合:降低程序的耦合度,提高程序的扩展力。
多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)

Animal a = new Cat();
Animal a = new Dog();
//喂养的方法
public void feed(Animal a){//面向父类型编程

}
//不建议:
Dog d = new Dog();
Cat c = new Cat();

3>JDBC开发准备

在这里插入图片描述

4>JDBC编程六步骤

  1. 注册驱动(作用:告诉JAVA程序,即将连接哪一个品牌数据库)
  2. 获取连接(表示JVM进程和数据库进程之间的通道打开了,这属于进程与进程之间的通信,重量级的,使用完一定要需要关闭)
  3. 获取数据库对象(专门执行SQL语句的对象)
  4. 执行SQL语言(DQL、DML、…)
  5. 处理查询结果集
  6. 释放资源
import java.lang.*;
import java.sql.*;

public class JDBCTest01{
	public static void main(String[] args){
		Connection connection = null;
		Statement stat = null;
		try{
		//1>注册驱动
		DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		//2>获取连接
		connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","111");
		//三个变量url user password
		//3>获取数据库操作对象
		stat = connection.createStatement();
		//4>执行SQL语句
		String sql = "delete from dept where deptno = 40";
		int count = stat.executeUpdate(sql);
		//executeUpdate()方法专门执行DML方法(insert delete update)
		//返回值是"影响数据库中的记录条数"
		System.out.println(count == 1?"执行成功":"执行失败");
		}
		catch(SQLException e){
			e.printStackTrace();
		}finally{
		//6>释放资源
		//从小到大释放资源
			if(stat!=null){
				try{
					stat.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
			if(connection!=null){
				try{
					connection.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
	}
}

4.1、第二种注册驱动方式(常用)

public static void main(String[] args){
	class.forName("com.mysql.jdbc.Driver");//反射机制
	//因为参数为字符串可以写道xxx.properties文件中
	//利用了反射机制 类记载的时候自动启动静态代码块
	//而静态代码块中帮助我们实现了第一种注册
}

4.2、使用资源绑定器绑定属性配置文件

import java.sql.*;
import java.util.*;
public class JDBCTest02{
	public static void main(String[] args){
		ResourceBundle  resourcebundle = ResourceBundle.getBundle("jdbc");
		String driver = resourcebundle.getString("driver");
		String url = resourcebundle.getString("url");
		String user = resourcebundle.getString("user");
		String password = resourcebundle.getString("password");
		Connection connection = null;
		Statement stat = null;
		try{
		//1>注册驱动
		Class.forName(driver);
		//2>获取连接
		connection = DriverManager.getConnection(url,user,password);
		//3>获取数据库操作对象
		stat = connection.createStatement();
		//4>执行SQL语句
		String sql = "update dept set dname ='销售部2',loc = '天津2' where deptno =20";
		int count = stat.executeUpdate(sql);
		System.out.println(count == 1?"修改成功":"修改失败");
		}catch(SQLException e){
			e.printStackTrace();
		}catch(ClassNotFoundException e){
			e.printStackTrace();
		}finally{
			try{
		//6>释放资源
			if(stat!=null){
				stat.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
			try{
			if(connection!=null){
				stat.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
}

4.3、遍历结果集

import java.sql.*;
import java.util.*;
public class JDBCTest03{
	public static void main(String[] args){
		ResourceBundle  resourcebundle = ResourceBundle.getBundle("jdbc");
		String driver = resourcebundle.getString("driver");
		String url = resourcebundle.getString("url");
		String user = resourcebundle.getString("user");
		String password = resourcebundle.getString("password");
		Connection connection = null;
		Statement stat = null;
		ResultSet rs = null;
		try{
		//1>注册驱动
		Class.forName(driver);
		//2>获取连接
		connection = DriverManager.getConnection(url,user,password);
		//3>获取数据库操作对象
		stat = connection.createStatement();
		//4>执行SQL语句
		String sql = "select emp,ename,sal from emp";
		//int executeUpdate(insert/delete/update)
		//ResultSet executeQuery(select) 返回查询结果集
		rs = stat.executeQuery(sql);
		//5>处理查询结果集
		while(rs.next()){
			 //getString()参数是列的下标也可以改成列名
			String empno = rs.getString(1);//第一列 第一字段
			String ename = rs.getString(2);//第二字段
			String sal = rs.getString(3);//第三字段
			//String empno = rs.getString("empno");//注意列名称不是表中的列名
			//String ename = rs.getString("ename");//而是查询结果集的列名
			//String sal = re.getString("sal");
			//建议改成字段名,因为数据库进行操作时可能会交换列
			System.out.println(empno+","+ename+","+sal);
		}
		}catch(SQLException e){
			e.printStackTrace();
		}catch(ClassNotFoundException e){
			e.printStackTrace();
		}finally{
		//6>释放资源
			try{
			if(rs!=null){
				re.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}
			try{
			if(stat!=null){
				stat.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
			try{
			if(connection!=null){
				stat.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
}

4.3.1、查询结果集衍生

  • 除了得到String以外也可以得到int double(前提是数字)
  • 针对于进行计算
    在这里插入图片描述

5>使用IDEA工具操作JDBC

  • 1、打开模块配置

在这里插入图片描述

  • 2、引入mysql驱动
    在这里插入图片描述

6>设置一个登录系统

6.1、利用PowerDesigner创建登录数据库表

  • 1、创建物理图
    在这里插入图片描述
  • 2、创建一个表
    在这里插入图片描述
  • 3、做出如下设置
    在这里插入图片描述
    在这里插入图片描述
  • 4、Ctrl+S 保存
    在这里插入图片描述
  • 5、保存sql文件
    在这里插入图片描述
  • 6、初步设置sql文件
    在这里插入图片描述
  • 7、把表导入数据库
use bjpowernode;
show tables;//确定不存在名为t_user的表
source +sql文件 

6.2、初步实现

import javax.xml.transform.Result;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JDBCTest01 {
    public static void main(String[] args) {
        Map<String,String> userLoginInfo = initUI();
        login(userLoginInfo);
    }

    public static Map<String, String> initUI() {
        Map<String,String> userLoginInfo = new HashMap<>();
        Scanner scanner = new Scanner(System.in);
        System.out.println("用户名:");
        String loginName = scanner.nextLine();
        System.out.println("密码:");
        String loginPwd = scanner.nextLine();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);
        return userLoginInfo;
    }
    public static void login(Map<String,String> userLoginInfo){
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        Connection con = null;
        Statement stat = null;
        ResultSet rs = null;
        try {//1>注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2>获取连接
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","111");
            //3>获取数据库操作对象
            stat = con.createStatement();
            //4>执行SQL语句
            String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
            //5>操作结果集
            rs = stat.executeQuery(sql);
            if(rs.next()){
                System.out.println("登录成功");
            }else{
                System.out.println("登陆失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            //6>释放连接
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (stat != null) {
                try {
                    stat.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

6.3、SQL注入问题

用户名:fdsa
密码:fdsa' or '1'='1
输入如上信息时,照样登录成功了
  • 通过debug查出此时sql语句为
    在这里插入图片描述
  • 这就是sql注入,通过在密码中写入sql相关语句从而达到破解登录的目的

6.4、如何解决sql注入

  • preparedStatament这个接口继承了java,sql.Statement
  • PreparedStatement是属于预编译的数据库操作对象
  • PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传"值"
  • 解决SQL注入的关键是:用户提供的信息中即使含有sql语句的关键字,但是这些关键字
    并不参与编译,不起作用
import javax.xml.transform.Result;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JDBCTest01 {
    public static void main(String[] args) {
        Map<String,String> userLoginInfo = initUI();
        login(userLoginInfo);
    }

    public static Map<String, String> initUI() {
        Map<String,String> userLoginInfo = new HashMap<>();
        Scanner scanner = new Scanner(System.in);
        System.out.println("用户名:");
        String loginName = scanner.nextLine();
        System.out.println("密码:");
        String loginPwd = scanner.nextLine();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);
        return userLoginInfo;
    }
    public static void login(Map<String,String> userLoginInfo){
        boolean flag = false;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {//1>注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2>获取连接
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","111");
            //3>获取数据库操作对象
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";
            ps = con.prepareStatement(sql);
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);
            //ps.setInt(1,xxx)这里还有setInt方法
            //4>操作SQL
            rs = ps.executeQuery();
            //5>操作结果集
            if(rs.next()){
                flag = true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            //6>释放连接
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            System.out.println(flag?"登陆成功":"登录失败");
        }
    }
}

6.5、Statement和PreparedStatement区别

  • Statement存在sql注入问题,PreparedStatement解决了SQL注入问题
  • Statement是编译一下执行一次。PreparedStatement是编译一次,可执行N次。(第二次输入时候与之前完全一样的sql语句,dbms不会编译,而是直接执行)所有PreparedStatement效率也更高
  • PreparedStatement会在编译阶段做类型的安全检查。
  • 99%的情况下是使用PreparedStatement

6.5.1、 什么时候使用Statement?

  • 业务方面要求必须支持SQL注入的时候
  • 例如需要根据输入的字符串选择升序还是降序时(浏览商品时,价格的升序降序)

7>使用JDBC完成数据的增删改

package ustc.java.jdbc;

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

/*
    使用PreparedStatement完成insert、update、delete
 */

public class JDBCTest10 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","146");
            // 3、获取预编译的数据库操作对象

            /*String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,60);
            ps.setString(2,"研发部");
            ps.setString(3,"北京");*/

            /*String sql = "update dept set dname = ?, loc = ? where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"销售二部");
            ps.setString(2,"西安");
            ps.setInt(3,60);*/

            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,60);

            // 4、执行sql语句
            int count = ps.executeUpdate();
            System.out.println(count == 1? "修改成功" : "修改失败");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 6、释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

8>JDBC的事务提交机制

  • 以下代码证明JDBC的事务提交机制为自动提交
  • 模拟转账A有2w,B有0元
  • 如果中途出现故障,由于自动提交机制
  • A转出1w给B,但B依然是0元
package ustc.java.jdbc;

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

public class JDBCTest11 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","111");

            // 获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ? ";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,10000);
            ps.setDouble(2,111);

            // 执行sql语句
            int count = ps.executeUpdate();

            String s = null;
            s.toString();

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

            System.out.println(count == 2 ? "转账成功" : "转账失败");
        } catch (Exception e) {
            e.printStackTrace();
        }  finally {
            // 释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}
  • 将自动提交改成手动提交,如果出现异常回滚
package ustc.java.jdbc;

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

public class JDBCTest12 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","146");

            // 将自动提交改为手动提交
            conn.setAutoCommit(false);//重要

            // 获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ? ";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,10000);
            ps.setDouble(2,111);

            // 执行sql语句
            int count = ps.executeUpdate();

            /*String s = null;
            s.toString();*/

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

            System.out.println(count == 2 ? "转账成功" : "转账失败");

            // 程序能执行到此处,说明没有异常,事务结束,手动提交数据
            conn.commit();//重要
        } catch (Exception e) {
            // 遇到异常,回滚
            if (conn != null) {
                try {
                    conn.rollback();//重要
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            e.printStackTrace();
        }  finally {
            // 释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

9>创建JDBC工具类

package ustc.java.jdbc.DBUtil;

import java.sql.*;

/*
    JDBC工具类,简化JDBC编程
*/
public class DBUtil {

    /**
     * 工具类中的构造方法是私有的
     * 因为工具类中的方法都是静态的,直接通过类名去调即可。
     */
    private DBUtil(){}

    /**
     * 静态代码块,类加载的时候执行
     * 把注册驱动程序的代码放在静态代码块中,避免多次获取连接对象时重复调用
     */
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","root","111");
    }

    public static void close(Connection conn, Statement ps, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

10>悲观锁和乐观锁

  • 悲观锁(行级锁):
    • select * from emp where job ='MANAGER' for update
    • 重要就是这个for update
    • 事务没结束之前,事务必须排队,一整行记录不准改动,不允许并发。
  • 乐观锁:支持并发,事务不排队,需要一个版本号。一个事务发现前后的版本号不一致了 就执行回滚操作 ,本次操作不执行。
    在这里插入图片描述
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值