JDBC(2) 工具类 | PreparedStatement详细说明


一、JDBC 工具类

使用 JDBC API 操作数据库的基本步骤

1、加载和注册驱动

2、获取数据库连接

3、准备操作执行 SQL 的 Statement 对象

4、执行 SQL
(1)调用 Statement 对象的 executeUpdate(String sql)执行 SQL 语句进行插入、修改、删除操作
(2)调用 Statement 对象的 executeQuery(String sql)执行 SQL 语句进行查询操作

5、处理执行结果
(1)CUD 操作,根据返回的 int 值判断结果
(2)查询操作,根据返回 ResultSet 结果集,获取查询数据

6、释放资源

总结:
(1)加载和注册驱动,整个项目做一次即可
(2)获取数据库连接可以封装到一个方法中
(3)释放资源可以封装到一个方法中

编写工具类 JDBCUtils

public class JDBCUtils {
	private static String drivername;
	private static String url;
	private static String user;
	private static String password;
	
	private static Properties pro = new Properties();
	
	static{
		try {
			//加载,读取 jdbc.properties 配置的信息
			//pro.load 的作用是把 jdbc.properties 文件中配置的信息,一一 put 到 pro 这个 map 中
			pro.load(ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"));
			// drivername = pro.getProperty("key")
			drivername = pro.getProperty("drivername");
			url = pro.getProperty("url");
			user = pro.getProperty("user");
			password = pro.getProperty("password");
			//注册驱动,加载驱动
			Class.forName(drivername);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static Connection getConnection()throws SQLException{
		Connection conn = DriverManager.getConnection(url, user, password);
		return conn;
	}
	
	public static void closeQuietly(Connection conn){
		try {
			if(conn!=null){
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public static void closeQuietly(Statement st){
		try {
			if(st!=null){
				st.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public static void closeQuietly(ResultSet rs){
		try {
			if(rs!=null){
				rs.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static void closeQuietly(Statement st,Connection conn){
		closeQuietly(st);
		closeQuietly(conn);
	}
	public static void closeQuietly(ResultSet rs,Statement st,Connection conn){
		closeQuietly(rs);
		closeQuietly(st);
		closeQuietly(conn);
	}
}

二、PreparedStatement

1 、PreparedStatement 概述

可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象
PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句

  • PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的
    setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从
    1 开始),第二个是设置的 SQL 语句中的参数的值
  • ResultSet executeQuery()执行查询,并返回该查询生成的 ResultSet 对象。
  • int executeUpdate():执行更新,包括增、删、该

2 、Statement 的不足

(1 )SQL 拼接

(2 )SQL 注入
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或
命令,从而利用系统的 SQL 引擎完成恶意行为的做法。对于 Java 而言,要防范 SQL 注入,只要用
PreparedStatement 取代 Statement 就可以了。

(3 )处理 Blob 类型的数据
BLOB (binary large object),二进制大对象,BLOB 常常是数据库中用来存储二进制文件的字段类型。
插入 BLOB 类型的数据必须使用 PreparedStatement,因为 BLOB 类型的数据无法使用字符串拼接写的。
MySQL 的四种 BLOB 类型(除了在存储的最大信息量上不同外,他们是等同的)

3 、PreparedStatement vs Statement

  • 代码的可读性和可维护性. Statement 的 sql 拼接是个难题。
  • PreparedStatement 可以防止 SQL 注入
  • PreparedStatement 可以处理 Blob 类型的数据
  • PreparedStatement 能最大可能提高性能: (Oracle 和 和 PostgreSQL8 是这样,但是对于 MySQL 不一定比 Statement高)
  • DBServer 会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被 DBServer 的编
    译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数
    直接传入编译过的语句执行代码中就会得到执行。
  • 在 statement 语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的
    意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.
  • (语法检查,语义检查,翻译成二进制命令,缓存)

4 、示例代码

(1 )使用 Statement
/*
* Statement:
* 1、SQL 拼接
* 2、SQL 注入
* 3、处理不了 Blob 类型的数据
*/
public class TestStatementProblem {
	@Test
	public void add() throws Exception{
		Scanner input = new Scanner(System.in);
		System.out.println("请输入姓名:");
		String name = input.nextLine();
		System.out.println("请输入领导编号:");
		int mid = input.nextInt();
		System.out.println("请输入部门编号:");
		int did = input.nextInt();
		//1、获取连接
		Connection conn = JDBCUtils.getConnection();
		//2、创建 Statement 对象
		Statement st = conn.createStatement();
		//3、编写 sql
		String sql = "INSERT INTO emp (ename,`mid`,did) VALUES('" + name+"'," + mid + "," + did + ")";
		//4、执行 sql
		int update = st.executeUpdate(sql);
		System.out.println(update>0?"添加成功":"添加失败");
		//5、释放资源
		JDBCUtils.closeQuietly(st, conn);
	}

	@Test
	public void select()throws Exception{
		Scanner input = new Scanner(System.in);
		System.out.println("请输入姓名:");
		String name = input.nextLine();
		//1、获取连接
		Connection conn = JDBCUtils.getConnection();
		//2、写 sql
		//孙红雷 ' or '1' = '1
		String sql = "SELECT eid,ename,tel,gender,salary FROM t_employee WHERE ename = '" + name + "'";
		System.out.println(sql);
		// SELECT eid,ename,tel,gender,salary FROM t_employee WHERE ename = '孙红雷 ' or '1' = '1'
		//3、用 Statement 执行
		Statement st = conn.createStatement();
		//4、执行查询 sql
		ResultSet rs = st.executeQuery(sql);
		while(rs.next()){
			int id = rs.getInt(1);
			String ename = rs.getString(2);
			String tel = rs.getString(3);
			String gender =rs.getString(4);
			double salary = rs.getDouble(5);
			System.out.println(id+"\t" + ename + "\t" + tel + "\t" + gender + "\t" +salary);
		}
		//5、释放资源
		JDBCUtils.closeQuietly(rs, st, conn);
	}
	
	@Test
	public void testAddBlob(){
		String sql = "INSERT INTO `user` (username,`password`,photo)VALUES('chai','123',没法在String中处理Blob类型的数据);";
	}
}
(2 )使用 PreparedStatement
/*
* PreparedStatement:是 Statement 子接口
* 1、SQL 不需要拼接
* 2、SQL 不会出现注入
* 3、可以处理 Blob 类型的数据
*/
public class TestPreparedStatement {
	@Test
	public void add() throws Exception {
	Scanner input = new Scanner(System.in);
	System.out.println("请输入姓名:");
	String name = input.nextLine();
	System.out.println("请输入性别:");
	String gender = input.nextLine();
	System.out.println("请输入领导编号:");
	int mid = input.nextInt();
	System.out.println("请输入部门编号:");
	int did = input.nextInt();
	String sql = "INSERT INTO emp VALUES(NULL,?,?,?,?)";// 参数,占位符,通配符,表示这个地方需要设置值
	// 2、获取连接
	Connection conn = JDBCUtils.getConnection();
	// 3、准备一个 PreparedStatement:预编译 sql
	PreparedStatement pst = conn.prepareStatement(sql);// 对带?的 sql 进行预编译
	// 4、把?用具体的值进行代替
	pst.setString(1, name);
	pst.setString(2, gender);
	pst.setInt(3, mid);
	pst.setInt(4, did);
	// 5、执行 sql
	int len = pst.executeUpdate();
	// 6、释放资源
	JDBCUtils.closeQuietly(pst, conn);
	}

	@Test
	public void select() throws Exception {
		// 3、写 sql
		Scanner input = new Scanner(System.in);
		System.out.println("请输入姓名:");
		String name = input.nextLine();
		// 孙红雷 ' or '1' = '1
		String sql = "SELECT eid,ename,tel,gender,salary FROM t_employee WHERE ename = ?";
		// 1、注册驱动,注册过了
		// 2、获取连接
		Connection conn = JDBCUtils.getConnection();
		// 3、把带?的 sql 语句进行预编译
		PreparedStatement pst = conn.prepareStatement(sql);
		// 4、把?用具体的变量的赋值
		pst.setString(1, name);
		// 5、执行 sql
		ResultSet rs = pst.executeQuery();
		while (rs.next()) {
			int id = rs.getInt("eid");
			String ename = rs.getString("ename");
			String tel = rs.getString("tel");
			String gender = rs.getString("gender");
			double salary = rs.getDouble("salary");
			System.out.println(id + "\t" + ename + "\t" + tel + "\t" + gender + "\t" + salary);
		}
		// 6、释放资源
		JDBCUtils.closeQuietly(rs, pst, conn);
	}
	@Test
	public void addBlob() throws Exception {
	Scanner input = new Scanner(System.in);
	System.out.println("请输入用户名:");
	String username = input.nextLine();
	System.out.println("请输入密码:");
	String password = input.nextLine();
	System.out.println("请指定照片的路径:");
	String photoPath = input.nextLine();
	// INSERT INTO `user` VALUES(NULL,用户名,密码,照片)
	String sql = "INSERT INTO `user` VALUES(NULL,?,?,?)";
	// 1、注册驱动,注册过了
	// 2、获取连接
	Connection conn = JDBCUtils.getConnection();
	// 3、准备一个 PreparedStatement:预编译 sql
	PreparedStatement pst = conn.prepareStatement(sql);// 对带?的 sql 进行预编译
	// 4、对?进行设置
	pst.setString(1, username);
	pst.setString(2, password);
	pst.setBlob(3, new FileInputStream(photoPath));
	// 5、执行 sql
	int len = pst.executeUpdate();
	System.out.println(len > 0 ? "添加成功" : "添加失败");
	// 6、释放资源
	JDBCUtils.closeQuietly(pst, conn);
	}
}

如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值