JDBC基础知识

导读:本篇文章讲解 JDBC基础知识,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

JDBC

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

JDBC的本质

JDBC是一套接口(在java.sql.*包下),接口都有调用者和实现者,面向接口调用、面向接口写实现类,都属于面向接口编程。

面向接口编程的好处:

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

JDBC编程

编程步骤:

  1. 注册驱动(告知要连接的是哪种类型数据库)
  2. 获取连接(打开JVM进程和数据库之间的通道,属于进程之间的通信,用完一定要关掉)
  3. 获取数据库操作对象(专门执行sql语句)
  4. 执行sql语句(主要执行DQL,DML…)
  5. 处理查询结果(第四步执行select语句才会有结果集)
  6. 释放资源(使用完资源之后一定要关闭资源)

url

url:统一资源定位符(某个资源的绝对路径)
url组成:协议、IP、PORT、资源名
jdbc:mysql://localhost:3306/mysql
jdbc:mysql://是协议
localhost是IP地址
3306是mysql数据库的端口号
mysql是具体的数据库实例名字
注:localhost和127.0.0.1都是本机ip地址
什么是通信协议:
通信协议是通信之前就提前定好数据传送格式
数据包具体怎么传数据,格式提前定好。

运用

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class Main {
	public static void main(String[] args) {
		Connection conn = null;
		Statement stmt = null;
		try {
			//1.注册驱动
			Driver driver;
			driver = new com.mysql.cj.jdbc.Driver();//使用多态,父类指向子类的对象,不同数据库Driver的包名也不同
			DriverManager.registerDriver(driver);
			//2.获取连接
			
			String url = "jdbc:mysql://localhost:3306/mysql";//数据库的位置
			String user = "root";//用户名
			String password = "123456";//密码
			conn = DriverManager.getConnection(url, user, password);//不管是mysql还是sqlserver,都用connection接口对象来调用即可
			//3.获取数据库操作对象(Statement专门执行sql语句)
			stmt = conn.createStatement();
			//4.执行sql语句
			String sql = "insert into dept(deptno,dname,loc) values('50','销售部','深圳')";
			//专门执行DML语句的insert,delete,update
			//返回值是影响数据库中的记录的条数
			int count = stmt.executeUpdate(sql);//多少条数据改变,则count就是多少
			//因为该sql语句不是select语句,所以不用处理查询结果集
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//6.释放资源
			//为了保证资源一定释放,在finally语句块中释放资源
			//并且要遵循从小到大依次关闭
			//关闭的时候分别进行trycatch因为堆在一个try里面的话,只关闭一个资源就会进入catch
			try {
				if(conn != null)
					conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if(stmt != null)
					stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}
}

注册驱动的第二种方式

可以使用类的加载的方式来注册驱动,通过使用类的反射机制来调用类中的静态方法

用法如下:

Class.forName(“com.mysql.cj.jdbc.Driver”);//该方法不用接受返回值,因为只需要用到其中类的加载动作

Connection conn = DriverManager.getConnection(url, user, password);

从属性资源文件中读取连接数据库的信息

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

public class Main {
	public static void main(String[] args) {
		Connection conn = null;
		Statement stmt = null;
		//使用资源绑定器绑定属性配置文件
		ResourceBundle bundle = ResourceBundle.getBundle("try2.jdbc");//注意配置文件的路径要写清楚所在包名,而且配置文件必须写在src包下
		String driver = bundle.getString("driver");
		String url = bundle.getString("url");
		String user = bundle.getString("user");
		String password = bundle.getString("password");
		/*再用上面获取到的数据按照jdbc编程的步骤即可*/

	}
}

配置文件jdbc.properties如下:

在这里插入图片描述

处理查询结果集

public class Main {
	public static void main(String[] args) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;//select查询出的结果集数据类型
		try {
			/*前面三步按照jdbc编程步骤来进行*/
            
			//4.执行DML语句
			String sql = "select * from emp ";
			//executeUpdate(insert/delete/update)
			//executeQuery(select)
			rs = stmt.executeQuery(sql);//rs则是select语句的结果集
			//5.处理查询结果集
			boolean flag1 = rs.next();//光标当前位置的下一个位置有数据则返回true,无则返回false
			// 光标指向的行有数据,光标初始位置从0开始,JDBC中所有下标从1开始。
			// 取数据
			// getString(列号/列名)	,如果列名有重命名则写重命名之后的列名
			//方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。
			String empno = rs.getString(1); 
			String ename = rs.getString("ename");
			//数据还可以以其相应的类型取出
			double sal =rs.getDouble(3);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//6.释放资源
			/*关闭资源从ResultSet到Statement到Connection*/
		}

	}
}

sql注入

什么是sql注入

当前登录数据库查询的sql语句为 String sql = “select * from t_user where loginName = ‘”+loginName+“’ and loginPwd = ‘”+loginPwd+“’”;

当用户loginName输入fdsa,loginPwd输入fdsa’ or ‘1’=’1则会成功,该现象就是sql注入现象。

出现的原因

用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。就是因为注入了or’1’=’1,从而使得输入账号密码正不正确都能成功登录。

解决方法

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

  •  即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
    
  •  要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
    
  •  **PreparedStatement**接口继承了java.sql.Statement
    
  •  **PreparedStatement**是属于预编译的数据库操作对象。
    
  •  **PreparedStatement**的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”
    
 	    // 打标记的意识
        boolean loginSuccess = false;
        // 单独定义变量
        String loginName = sc.nextLine();
        String loginPwd = sc.nextLine();

        // JDBC代码
        Connection conn = null;
        PreparedStatement ps = null; // 这里使用PreparedStatement(预编译的数据库操作对象)
        ResultSet rs = null;

        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
            // 3、获取预编译的数据库操作对象
            // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来,所以不能传字符或者字符串
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";
            // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
            ps = conn.prepareStatement(sql);
            // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
            ps.setString(1, loginName);
            ps.setString(2, loginPwd);
            // 4、执行sql
            rs = ps.executeQuery();
            // 5、处理结果集
            if(rs.next()){
                // 登录成功
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6、释放资源
        }

对比一下Statement和PreparedStatement

  • Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。
  • Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次。PreparedStatement效率较高一些。
  • PreparedStatement会在编译阶段做类型的安全检查.

必须使用Statement的情况

Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的(需要客户输入的数据是字符或者字符串且得直接拼接到sql语句,就是说不是=某个字符串这种类型,如升序或者降序这种),则必须使用Statement。

eg: String sql = “select ename from emp order by ename ?”;如果想要降序则问号得输入desc,而输入的话会有双引号,导致语句出现错误。

事务

因为java程序中机制为自动提交机制,即语句一行一行执行,则当中途出现错误时,可能造成数据丢失。

  1. 设置手动提交机制conn.setAutoCommit(false);因为默认值是true
  2. 事务提交conn.commit();语句全部成功执行在提交
  3. 事务回滚conn.rollback();发生异常时撤销全部操作
 		Connection conn = null;
        PreparedStatement ps = null;
        try {
            /*正常注册驱动获取连接*/
            // 将自动提交机制修改为手动提交
            conn.setAutoCommit(false); // 开启事务
            // 3、获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ?";
            ps = conn.prepareStatement(sql);
            //执行任务一
            ps.setDouble(1, 10000);
            ps.setInt(2, 111);
            int count = ps.executeUpdate();

            //执行任务二
            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 e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 6、释放资源
        }

简化jdbc编程

一些注册驱动获取连接等操作泰国冗余,则可以对其进行封装,封装好的工具类。

import java.sql.*;
public class DBUtil {
    /**
     * 工具类中的构造方法都是私有的。
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
     */
    private DBUtil() {
    }

    // 静态代码块在类加载时执行,并且只执行一次。
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


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

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

}

应用:

import com.bjpowernode.jdbc.utils.DBUtil;//导入工具类所在的包
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

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

    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84195.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!