Java数据库连接学习笔记

学习视频链接:B站 动力节点

基于MySql数据库学习JDBC。

准备:

​ 驱动jar包下载:jar包下载

​ 配置坏境变量:(文本编辑器,开发工具配置使用其相应配置方式)将jar包配置到classpath当中,classpath=.;jar包绝对路径

Java数据库连接(Java Database Connectivity,简称JDBC)。

一、JDBC编程六步

1、注册驱动
2、获取连接(使用完后需关闭)
3、获取数据库操作对象
4、执行SQL语句
5、处理查询结果集(只有第4步执行的是select语句的时候,才有第5步的处理查询结果集)
6、释放资源(关闭资源)

1、注册驱动

/*
    java.sql.Driver driver = new com.mysql.jdbc.Driver(); //多态,父类型引用指向子类型
    DriverManager.registerDriver(driver);
    //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
*/
//类加载的方式注册驱动
Class.forName("com.mysql.jdbc.Driver"); //存在类加载异常
/*
	String driver = "com.mysql.jdbc.Driver";
	Class.forName(driver);
*/

2、获取连接

String url = "jdbc:mysql://ip:port/数据库名";
String user = "用户名";
String password = "密码";
Connection connection = DriverManager.getConnection(url,user,password);

3、获取数据库操作对象

Statement stmt = conn.createStatement();

4、执行SQL语句

String sql = "sql语句";
//sql语句如果是DML语句(insert、delete、update)
int conunt = stmt.executeUpdate(sql);  //返回值是影响数据库中的记录条数
//sql语句是DQL语句(select)
ResultSet rs = stmt.executeQuery(sql); 

5、处理查询结果集

//若第四步的sql语句是DQL,则执行该步骤,反正,跳过该步骤
while(rs.next()){
    //JDBC中所有下标从1开始
    //getString()方法的特点:无论数据库中的数据类型是什么,都以String形式取出
    /*
    	String 变量名 = rs.getString(列的下标); //rs.getSring(1),第一列
    */
    String 变量名 = rs.getString("列名"); //列名是查询结果集里的列名
    //也可以以特定的类型取出,例如getInt()
}

6、释放资源

try{
    shu
}catch(SQLException e){
    e.printStackTrace();
}finally{
    //为了保证资源一定释放,在finally语句块中关闭资源,遵循从小到大一次关闭
    /*
    	try{
    		if(rs != null){
    			rs.close();
    		}
    	}catch(SQLException e){
    		e.printStackTrace();
    	}
    */
    try{
       if(stmt != null){
           stmt.close();
       } 
    }catch(Exception e){
        e.printStackTrace();
    }
    try{
        if(conn != null){
            conn.close();
        }
    }catch(SQLException e){
        e.printStackTrace();
    }
}

二、SQL注入

package com.lskj.jdbc;

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

/**
 * @author lskj
 * @create 2020/5/18 - 13:44
 * 模拟用户登录功能的实现
 */
public class JDBCTest {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        //验证用户名和密码
        boolean LoginResult = login(userLoginInfo);
        System.out.println(LoginResult ? "登陆成功!" : "登陆失败!");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示登录失败,true表示登陆成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String pwd = "root";
        Connection conn = null;
        Statement stmt = null;
        String loginName = userLoginInfo.get("username");
        String loginPwd = userLoginInfo.get("userpwd");
        ResultSet rs = null;
        Boolean loginSuccess = false;
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            conn = DriverManager.getConnection(url,user,pwd);
            //获取数据库操作对象
            stmt = conn.createStatement();
            //执行SQL
            String sql = "select * from t_user where loginName ='"+loginName+"' and loginPwd ='"+loginPwd+"'";
            rs = stmt.executeQuery(sql);
            System.out.println(sql);
            //处理结果集
            /*while(rs.next()){
                System.out.println(rs.getString(2)+rs.getString(3));
            }*/
            if(rs.next()){
                //登陆成功
                loginSuccess = true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            try {
                if(rs != null){
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(stmt != null){
                    stmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s  = new Scanner(System.in);

        System.out.print("用户名:");
        String username = s.nextLine();

        System.out.print("密码:");
        String userpwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("username",username);
        userLoginInfo.put("userpwd",userpwd);

        return  userLoginInfo;
    }
}

对于以上代码,存在SQL注入。

例如:格式类似于用户名输入1,密码输入1’ or ‘1’='1,t_user表中不存在该用户的信息,但是仍能登陆成功。
在这里插入图片描述

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

因此,只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。

要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PrepareStatement

PrepareStatement接口继承了java.sql.Statement
PrepareStatement是属于预编译的数据库操作对象。
PrepareStatement的原理:
	预先对SQL语句的框架进行编译,然后再给SQL语句传值。
package com.lskj.jdbc;

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

/**
 * @author lskj
 * @create 2020/5/18 - 13:44
 * 模拟用户登录功能的实现
 */
public class JDBCTest {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        //验证用户名和密码
        boolean LoginResult = login(userLoginInfo);
        System.out.println(LoginResult ? "登陆成功!" : "登陆失败!");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示登录失败,true表示登陆成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String pwd = "root";
        Connection conn = null;
        PreparedStatement ps= null;  //防止SQL注入,故使用PreparedStatement(预编译的数据库操作对象)
        String loginName = userLoginInfo.get("username");
        String loginPwd = userLoginInfo.get("userpwd");
        ResultSet rs = null;
        Boolean loginSuccess = false;
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //获取连接
            conn = DriverManager.getConnection(url,user,pwd);
            //获取预编译的数据库操作对象
            //?表示占位符,占位符不能使用单引号括起来。
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";  //sql语句框架。
            ps = conn.prepareStatement(sql);
            //给占位符传值
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);
            System.out.println("SQL语句为"+sql);
            //执行SQL
            rs = ps.executeQuery();

            //处理结果集
            /*while(rs.next()){
                System.out.println(rs.getString(2)+rs.getString(3));
            }*/
            if(rs.next()){
                //登陆成功
                loginSuccess = true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            try {
                if(rs != null){
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(ps!= null){
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s  = new Scanner(System.in);

        System.out.print("用户名:");
        String username = s.nextLine();

        System.out.print("密码:");
        String userpwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("username",username);
        userLoginInfo.put("userpwd",userpwd);

        return  userLoginInfo;
    }
}

在这里插入图片描述

Statement与PreparedStatement:
	Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题。
	Statement是编译一次执行一次,PrepareStatement是编译一次,可执行n次。
	PrepareStatement会在编译阶段做类型的安全检查。
必须使用Statement的情况:
	业务方面要求必须使用SQL注入的时候。
	Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。

三、事务机制

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

package com.lskj.jdbc;

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

/**
 * @author lskj
 * @create 2020/5/18 - 18:22
 */
public class BankTransfer {
    public static void main(String[] args) {
        String driver = "com.mysql.jdbc.Driver";
        String uri = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "root";
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(uri,user,password);
            //将自动提交机制修改为手动提交
            conn.setAutoCommit(false);
            String sql = "update t_bank set balance = ? where actno = ?";
            ps = conn.prepareStatement(sql);
            ps.setDouble(1,10000);
            ps.setInt(2,111);
            int count = ps.executeUpdate();

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

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

            System.out.println(count==2?"转账成功!" : "转账失败!");
            //事务结束,手动提交
            conn.commit();
        }catch (Exception e){
            //回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        }finally {
            try {
                if (ps != null) {
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
conn.setAutoCommit(false);    //将自动提交事务修改为手动提交
conn.commit();   //手动提交事务
conn.rollback();  //事务回滚

四、JDBC工具类,简化JDBC编程

package com.lskj.jdbc;

import java.sql.*;

/**
 * @author lskj
 * @create 2020/5/19 - 9:36
 * JDBC工具类,简化JDBC编程
 */
public class JDBCUtil {
    //工具类的构造方法都是私有的,
    // 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用
    private JDBCUtil() {
    }
    //静态代码块在类加载时执行,并且只执行一次
    static {
        try {
            //注册驱动
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 关闭资源
     * @param conn 连接对象
     * @param stmt 数据库操作对象
     * @param rs 查询结果集
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

package com.lskj.jdbc;

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

/**
 * @author lskj
 * @create 2020/5/19 - 9:38
 *
 * 测试JDBCUtil,同时实现模糊查询
 */
public class FuzzyQuery {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //获取连接
            conn = JDBCUtil.getConnection();
            //获取预编译的数据库操作对象
            String sql = "select dname from dept where dname like ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"%部");
            //执行sql语句
            rs = ps.executeQuery();
            //处理查询结果集
            while(rs.next()){
                String dname = rs.getString("dname");
                System.out.println(dname);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCUtil.close(conn,ps,rs);
        }
    }
}

五、行级锁

行级锁(悲观锁):事务必须排队执行。数据被锁住,不允许并发。

乐观锁:支持并发。事务不需要排队。只不过需要一个版本号。

在这里插入图片描述

package com.lskj.jdbc;

import java.sql.*;

/**
 * @author lskj
 * @create 2020/5/19 - 10:26
 * 开启一个事务(进行查询,使用行级锁),锁住相关记录
 */
public class Query {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //获取数据库连接
            conn = JDBCUtil.getConnection();
            //开启事务(关闭事务自动提交)
            conn.setAutoCommit(false);
            //获取预编译的数据库操作对象
            String sql = "select ename,job,sal from emp where job = ? for update";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"CLERK");
            //执行sql语句
            rs = ps.executeQuery();
            //处理查询结果集
            while (rs.next()){
                String ename = rs.getString("ename");
                String job = rs.getString("job");
                String sal = rs.getString("sal");
                System.out.println(ename+"\t"+job+"\t"+sal);
            }
            //提交事务(事务结束)
            conn.commit();  //此处加断点进行测试
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCUtil.close(conn,ps,rs);
        }
    }
}

package com.lskj.jdbc;

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

/**
 * @author lskj
 * @create 2020/5/19 - 10:27
 * 修改被锁定的记录
 */
public class Update {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            //过去数据库连接对象
            conn = JDBCUtil.getConnection();
            //开启事务(关闭事务自动提交)
            conn.setAutoCommit(false);

            String sql = "update emp set sal = sal * 1.1 where job = ?";
            //获取预编译的数据库操作对象
            ps = conn.prepareStatement(sql);
            ps.setString(1,"CLERK");
            //执行sql语句
            int count = ps.executeUpdate();
            System.out.println(count);

            //提交事务
            conn.commit();
        } catch (SQLException e) {
            //回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCUtil.close(conn,ps,null);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值