JDBC学习记录

下载jar包配置环境变量

  1. 在官网中下载java链接数据库的驱动,用什么数据库就去哪里下载
    在这里插入图片描述
  2. 将下载的驱动解压配置到环境变量的classpath中,使用IDEA不用配置,IDEA有专门的导入方式
    在这里插入图片描述

JDBC链接数据库的六个步骤

  1. 注册数据库驱动
//方式一:手动注册(不常用)
Driver driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);
//方式二:使用反射机制自动注册
//原因是在获取类时,会自动运行类中的静态代码块,而com.mysql.cj.jdbc.Driver的静态代码块中会自动注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
  1. 获取数据库连接对象
//2 获取数据库连接
String url = "jdbc:mysql://localhost:3306/***"; //***数据库名
String user = "***"; //用户名
String password = "***";  //密码
Connection conn = DriverManager.getConnection(url, user, password);
  1. 获取数据库操作对象
//3 获取数据库操作对象
Statement sm = conn.createStatement();
  1. 执行SQL语句
//4 执行SQL语句
String sql = "select * from dept";
//ResultSet executeQuery(String sql) 
//执行给定的SQL语句,该语句返回单个 ResultSet对象。查询语句  
//int executeUpdate(String sql) 
//执行给定的SQL语句,这可能是 INSERT , UPDATE ,或 DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句。  返回值是影响记录的行数

rs = sm.executeQuery(sql);
  1. 处理查询结果集
//5 处理查询结果集
//只有执行查询语句时,才有结果集
while(rs.next()){
	System.out.println(rs.getString("deptno")+","+rs.getString("dname")+","+rs.getString("loc"));
}
  1. 关闭连接
//6 关闭连接 从内到外
			if(rs != null){
				try{
					rs.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
			
			if(sm != null){
				try{
					sm.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
			
			if(conn != null){
				try{
					conn.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}

完整流程

import java.sql.*;


public class MySqlTest{
	public static void main(String[] args){
		Connection conn = null;
		Statement sm = null;
		ResultSet rs = null;
		try{
			//1 注册数据库驱动
			//方式一:手动注册(不常用)
			//Driver driver = new com.mysql.cj.jdbc.Driver();
			//DriverManager.registerDriver(driver);
			//方式二:使用反射机制自动注册,原因是在获取类时,会自动运行类中的静态代码块,而com.mysql.cj.jdbc.Driver的静态代码块中会自动注册驱动
			Class.forName("com.mysql.cj.jdbc.Driver");
			//2 获取数据库连接
			String url = "jdbc:mysql://localhost:3306/wcy";
			String user = "root";
			String password = "root";
			conn = DriverManager.getConnection(url, user, password);
			System.out.println("数据库操作对象:"+conn);
			//3 获取数据库操作对象
			sm = conn.createStatement();
			//4 执行SQL语句
			String sql = "select * from dept";
			rs = sm.executeQuery(sql);
			//5 处理查询结果集
			while(rs.next()){
				System.out.println(rs.getString("deptno")+","+rs.getString("dname")+","+rs.getString("loc"));
			}
		}catch(Exception e){
		
		}finally{
			//6 关闭连接 从内到外
			if(rs != null){
				try{
					rs.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
			
			if(sm != null){
				try{
					sm.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
			
			if(conn != null){
				try{
					conn.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
			
		}
	}
}

模拟用户登陆

package com.wcy.code01;

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

/**
 * JDBC的使用
 * 模拟一个登陆功能
 */
public class JdbcTest01 {
    public static void main(String[] args) {
        Map<String, String> userInfo = getUserInput();
        boolean isLogin = login(userInfo);
        System.out.println(isLogin ? "登陆成功" : "登陆失败");
    }

    /**
     * 用户登陆验证
     * @param userInfo:用户输入的信息
     * @return boolean 登陆是否成功
     */
    private static boolean login(Map<String, String> userInfo) {
        boolean isLogin = false;
        String username = userInfo.get("username");
        String password = userInfo.get("password");
        Connection conn = null;
        Statement sm = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/***", "***", "***");
            sm = conn.createStatement();
            //TODO sql注入问题
            //当密码输入为 fdsfas' or '1'='1时 也能登陆成功
            String sql = "select * from userinfo where username='" + username + "' and password='" + password + "'";
            rs = sm.executeQuery(sql);
            if (rs.next()) {
                isLogin = true;
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (sm != null) {
                try {
                    sm.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }


        return isLogin;
    }

    /**
     * 接收用户输入
     * @return userInfo:返回用户输入信息的Map
     */
    private static HashMap<String, String> getUserInput() {
        HashMap<String, String> userInfo = new HashMap<>();
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String username = scanner.nextLine();
        System.out.print("请输入密码:");
        String password = scanner.nextLine();
        userInfo.put("username", username);
        userInfo.put("password", password);
        return userInfo;
    }


}

当前该程序存在一个问题
当密码输入为 fdsfas’ or ‘1’='1时 也能登陆成功
这种现象被称为sql注入 通过用户的输入来破坏原来的SQL语句
在这里插入图片描述
当用户的输入为上述时 SQL语句变成如下

select * from userinfo where username='111' and password='fsdfas or '1'='1';

后面的条件恒成立,所以可以查询到结果。

如何避免SQL注入问题

  • 导致SQL注入的根本原因
    用户的输入中含有SQL语句的关键字,并且这些关键字参与了SQL语句的编译过程,导致SQL语句的原意被扭曲,从而到达了SQL注入的问题。
  • 解决SQL注入问题
    只要不让用户输入的信息不参与SQL编译的过程,就可以解决
    即使用户输入含有SQL关键字,没有参与编译也不起作用
    使用java.sql.PreparedStatement
    PreparedStatement是属于预编译数据库操作对象
    PreparedStatement的原理:预先编译SQL语句的框架,然后在给SQL语句传值,即使用户输入有关键字也不编译
package com.wcy.code01;

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

/**
 * 解决SQL注入问题
 *      只要不让用户输入的信息不参与SQL编译的过程,就可以解决
 *      即使用户输入含有SQL关键字,没有参与编译也不起作用
 *      使用java.sql.PreparedStatement
 *      PreparedStatement是属于预编译数据库操作对象
 *      PreparedStatement的原理:预先编译SQL语句的框架,然后在给SQL语句传值,即使用户输入有关键字也不编译
 *
 * Statement和PreparedStatement对比
 *      Statement存在SQL注入问题,PreparedStatement不存在
 *      Statement编译一次执行一次,PreparedStatement编译一次执行n次,效率较高
 *      PreparedStatement在编译阶段会做类型的安全检查
 *
 * 大多数情况下我们都使用PreparedStatement,少数情况下使用Statement
 *      在业务方面要求支持SQL注入时,就必须使用Statement
 *      例如desc和asc
 */
public class JdbcTest02 {
    public static void main(String[] args) {
        Map<String, String> userInfo = getUserInput();
        boolean isLogin = login(userInfo);
        System.out.println(isLogin ? "登陆成功" : "登陆失败");
    }

    /**
     * 用户登陆验证
     * @param userInfo:用户输入的信息
     * @return boolean 登陆是否成功
     */
    private static boolean login(Map<String, String> userInfo) {
        boolean isLogin = false;
        String username = userInfo.get("username");
        String password = userInfo.get("password");
        Connection conn = null;
        //使用数据库预编译对象
        PreparedStatement psm = null;
        ResultSet rs = null;
        try {
            //1 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2 获取数据库连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/wcy", "root", "root");

            //3 获取数据库预编译对象
            //这里要提前准备sql语句模板,需要用户输入的地方使用占位符?进行占位,不能用单引号括起来'?',这样会认为是普通的字符
            String sql = "select * from userinfo where username=? and password=?";
            //这里会把sql语句模板发送给数据库完成预先编译
            psm = conn.prepareStatement(sql);
            //给占位符传值,使用下标来传值,jdbc中所有的下标从1开始!!
            //使用一系列set方法,setInt(),setString....
            //使用setString()方法会自动将输入的信息用单引号括起来传入
            psm.setString(1, username);
            psm.setString(2, password);

            //4 执行Sql, 这里不能再传入sql语句,否则会再编译一次sql语句,前面的操作全部作废
            rs = psm.executeQuery();

            //5处理查询结果
            if (rs.next()) {
                isLogin = true;
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            //6 关闭连接 释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (psm != null) {
                try {
                    psm.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }


        return isLogin;
    }

    /**
     * 接收用户输入
     * @return userInfo:返回用户输入信息的Map
     */
    private static HashMap<String, String> getUserInput() {
        HashMap<String, String> userInfo = new HashMap<>();
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String username = scanner.nextLine();
        System.out.print("请输入密码:");
        String password = scanner.nextLine();
        userInfo.put("username", username);
        userInfo.put("password", password);
        return userInfo;
    }
}

使用PrepareStatement执行增删改操作

package com.wcy.code01;

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

public class JdbcTest03 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement psm = null;
        Scanner scanner = new Scanner(System.in);

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/**", "**", "**");
            String sql = "insert into userinfo(id, username, password) values(?, ?, ?)";
            psm = conn.prepareStatement(sql);

            psm.setInt(1, 3);
            psm.setString(2, "hhh");
            psm.setString(3, "123424");

            //返回结果是影响记录的条数
            int count = psm.executeUpdate();
            System.out.println(count);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            if (psm != null) {
                try {
                    psm.close();
                } catch (SQLException throwable) {
                    throwable.printStackTrace();
                }
            }

            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwable) {
                    throwable.printStackTrace();
                }
            }
        }
    }
}

String sql = "update userinfo set username=?, password=? where id=3";
            psm = conn.prepareStatement(sql);

            psm.setString(1, "sdfsdf");
            psm.setString(2, "dfsadfs");

String sql = "delete from userinfo where id=?";
            psm = conn.prepareStatement(sql);

            psm.setInt(1, 3);


            //返回结果是影响记录的条数
            int count = psm.executeUpdate();

jdbc事务提交机制

  • jdbc中的事务是自动提交的

  • 只要执行任意一条DML语句,则自动提交一次,这是jdbc默认的事务行为

  • 在实际的业务中,通常都是n条DML语句共同联合完成,必须保证这些语句在同一个事务中同时成功或者是同时失败

  • jdbc事务控制
    1. conn.setAutoCommit(flase); 关闭自动提交
    2. conn.commit(); 提交事务
    3. conn.rollback(); 回滚事务

  • 模拟账户转账

package com.wcy.code02;

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

/**
 * 模拟账户转账
 * 使用事手动提交事务
 */
public class JdbcCommitTest01 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement psm = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/wcy", "root", "root");
            //关闭自动提交
            conn.setAutoCommit(false);
            String sql = "update userinfo set balance=? where id=?";
            psm = conn.prepareStatement(sql);

            //用户1转出10000到用户2
            psm.setDouble(1,10000);
            psm.setInt(2,1);
            int count = psm.executeUpdate();

            //用户2收到10000
            psm.setDouble(1,10000);
            psm.setInt(2,2);
            count += psm.executeUpdate();

            //同时提交成功就提交事务
            if(count==2){
                conn.commit();
            }else {
                conn.rollback();
            }


        } catch (ClassNotFoundException | SQLException e) {
            try {
                if (conn != null) {
                    //发生异常也回滚
                    conn.rollback();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}

封装jdbc代码

package com.wcy.code02;

import java.sql.*;

/**
 * 封装数据库的连接的工具包
 */
public class DBUtil {
    //注册驱动
    //驱动只需要注册一次 所以写在静态代码块中
    //只要加载该类时就执行
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //连接数据库 并返回数据库连接对象
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/*", "*", "*");
    }

    //关闭连接
    public static void close(Connection conn, Statement sm, ResultSet rs){
        if (rs != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (sm != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

模糊查询

package com.wcy.code02;

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

/**
 * jdbc的模糊查询
 */
public class JdbcTest04 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement psm = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            String sql = "select * from dept where loc like ?";
            psm = conn.prepareStatement(sql);
            // 和其他的传参一样
            psm.setString(1, "_E%");
            rs = psm.executeQuery();

            while (rs.next()){
                System.out.println(rs.getString("loc"));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, psm, rs);
        }
    }
}

悲观锁和乐观锁

  • 悲观锁(行级锁)
    事务必须要排队执行,数据被锁住,不能并发,数据只能被一个事务执行, 在select语句后添加for update即可。
  • 乐观锁
  • 数据支撑并发,事务也不需要排队,只不过每个事务需要获得一个版本号,如果数据提交前后版本号不一致,就回滚
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值