25-JDBC-编程进阶

本文深入探讨JDBC的PrepareStatement使用,包括插入、删除、更新和查询数据,强调其防止SQL注入和批量执行的优势。同时,讲解数据库事务的ACID特性,并介绍JDBC中如何管理事务。最后,讨论了JDBC元数据的获取,以获取数据库和ResultSet的相关信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDBC编程进阶

第一节:PrepareStatement的使用

本节目标:

  • 理解Statement操作数据库存在的问题
  • 理解SQL注入问题
  • 掌握使用PrepareStatement操作数据库

PrepareStatement表示一个预编译SQL语句对象,我们通过prepareStatement对象excuteXXX对该SQL语句执行。我们在生成PrepareStatement的时候需要传递一个SQL语句进行预编译。在传递的SQL语句中如果有参数,我们将该通过?进行占位。然后通过PrepareStatement 提供的SetXXX(index,values)进行设值,其中index表示参数的位置(位置从1开始)

1、怎么得到PS的对象

PreparedStatement ps = conn.prepareStatement(sql);

2、怎么写SQL语句

String sql = "INSERT INTO student(`name`,`desc`) VALUES(?,?)";

3、怎么设值

//4.设置值  setXXX(index,value) 其中XXX的含义是参数是什么数据类型此处就要对应什么数据类型  index从1开始计算
ps.setString(1, "张三");
ps.setString(2, "张三又违法啦");

4、怎么执行SQL

//5.执行 prepareStatement对象中有 excuteUpdate-- insert update delete  excueteQuery--select
int count = ps.executeUpdate();//返回值表示受影响的行数
插入数据
//1.获取数据库连接
Connection conn = DBUtil.getConnection();
//2.准备SQL语句 想学生表中插入一条数据
String sql = "INSERT INTO student(`name`,`desc`) VALUES(?,?)";
//3.预编译SQL(?占位)
PreparedStatement ps = conn.prepareStatement(sql);
//4.设置值  setXXX(index,value) 其中XXX的含义是参数是什么数据类型此处就要对应什么数据类型  index从1开始计算
ps.setString(1, "张三");
ps.setString(2, "张三又违法啦");
//5.执行 prepareStatement对象中有 excuteUpdate-- insert update delete  excueteQuery--select
int count = ps.executeUpdate();//返回值表示受影响的行数
//6.解析结果
if(count>0) {
    System.out.println("执行成功");
}
//7.关闭资源
ps.close();
conn.close();
删除数据

	public static void main(String[] args) throws SQLException{
		//1.获取数据库连接
		Connection conn = DBUtil.getConnection();
		//2.准备SQL语句
		//UPDATE student SET `name`='修改的',`desc`='我是修改的' WHERE id=5
		String sql = "DELETE FROM student WHERE id=?";
		//3.预编译SQL
		PreparedStatement ps = conn.prepareStatement(sql);
		//4.设置值 setXXX(index,value)  index表示第几个问号 value表示值
		ps.setInt(1, 5);
		//执行
		int count = ps.executeUpdate();
		System.out.println(count);
		//关闭资源
		ps.close();
		conn.close();
	}

更新数据

	public static void main(String[] args) throws SQLException{
		//1.获取数据库连接
		Connection conn = DBUtil.getConnection();
		//2.准备SQL语句
		//UPDATE student SET `name`='修改的',`desc`='我是修改的' WHERE id=5
		String sql = "UPDATE student SET `name`=?,`desc`=? WHERE id=?";
		//3.预编译SQL
		PreparedStatement ps = conn.prepareStatement(sql);
		//4.设置值 setXXX(index,value)  index表示第几个问号 value表示值
		ps.setString(1, "李四");
		ps.setString(2, "李四是谁");
		ps.setObject(3, "5");
		//执行
		int count = ps.executeUpdate();
		System.out.println(count);
		//关闭资源
		ps.close();
		conn.close();
	}

查询数据
public static void main(String[] args) throws Exception {
	//1.获取连接
	Connection conn = DBUtil.getConnection();
	//2.准备SQL
	String sql = "SELECT * FROM student";
	//3.预编译SQL
	PreparedStatement ps = conn.prepareStatement(sql);
	//4.执行
	ResultSet rs = ps.executeQuery();
	//5.解析
	while(rs.next()) {
		System.out.println(rs.getInt("id")); //getXXX(index)  表示
		System.out.println(rs.getString("name"));//getXXX(列名)
		System.out.println(rs.getObject("desc"));
	}
	ps.close();
	conn.close();
}

注意:查询中解析结果的时候 getXXX方法进行解析

​ getXXX(index) 表示通过写的SQL语句查出的那张表里面index表示列数(从1开始)

​ getXXX(clounmName) 根据指定列名查结果

prepareStatement操作数据库标准的写法

	public static void main(String[] args){
		//1.获取连接
		Connection conn = DBUtil.getConnection();
		//2.准备SQL
		String sql = "SELECT * FROM student";
		//3.预编译SQL
		PreparedStatement ps = null;
		try {
			ps = conn.prepareStatement(sql);
			//4.执行
			ResultSet rs = ps.executeQuery();
			//5.解析
			while(rs.next()) {
				System.out.println(rs.getInt("id")); //getXXX(index)  表示
				System.out.println(rs.getString("name"));//getXXX(列名)
				System.out.println(rs.getObject("desc"));
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(ps!=null) {
				try {
					ps.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
1.1、Statement操作数据库存在的问题(设值参数是拼接SQL的问题)

1.我们在插入数据,修改数据的时候我们的数据如果从前台获取,我们手动的拼接SQL语句。

如果我们使用prepareStatement可以解决拼接SQL的痛苦。

1.2、PrepareStatement可以有效的防止SQL注入

什么是SQL注入:

就是攻击者通过一定的手段,将数据进行了修改,导致数据成为SQL的一部分执行了,达到了欺骗服务器和数据库的效果,从而绕过了验证。

SELECT * FROM t_user WHERE `name`=''OR 1=1 OR'' AND pass='123456'

怎么防止SQL注入:

1、对数据进行加密

2、PrepareStatement可以有效的防止SQL注入

/**
 * PrepareStatement可以有效防止SQL注入
 * 为什么:我们PS来操作数据库的时候,我们先对SQL进行了预编译,一定预编译成功
 * 这个SQL就确定了,后续参数不会影响SQL本身结构
 * @author Administrator
 *
 */
//登录逻辑
public class TestLoginByPrepareStatement {
	public static void main(String[] args) throws SQLException {
		String uname="'OR 1=1 OR'";
		String pass="123456";
		//1.获取连接
		Connection conn =  DBUtil.getConnection();
		//2.准备SQL
		String sql = "SELECT * FROM t_user WHERE name=? AND pass=?";
		//3.预编译SQL
		PreparedStatement ps = conn.prepareStatement(sql);
		//4.设置值
		ps.setString(1, uname);
		ps.setString(2, pass);
		//5.执行
		ResultSet rs = ps.executeQuery();
		//6.处理
		if(rs.next()) {
			System.out.println("登录成功");
		}else {
			System.out.println("登录失败");
		}
		//7.关闭相关资源
		ps.close();
		conn.close();
	}
1.3、PrepareStatement可以批量执行

可以一次执行多条语句,提高执行的效率。


		Connection conn = DBUtil.getConnection();
		//准备SQL
		String sql = "INSERT INTO student VALUES(DEFAULT,?,?)";
		//预编译SQL
		PreparedStatement ps = conn.prepareStatement(sql);
		
		ps.setString(1, "A");
		ps.setString(2, "AA");
		//可以囤数据
		ps.addBatch();
		
		ps.setString(1, "B");
		ps.setString(2, "BB");
		ps.addBatch();
		
		ps.setString(1, "C");
		ps.setString(2, "CC");
		ps.addBatch();
		
		//将囤积的数据一次执行(批量执行)
		int[] executeBatch = ps.executeBatch();
		
		for (int i : executeBatch) {
			System.out.println(i);
		}
		ps.close();
		conn.close();
	

第二节:数据库事务

本节目标

  • 理解事务的概念
  • 掌握事务的ACID四大特性
  • 理解并发场景下事务问题
  • 掌握数据库事务隔离级别
  • JDBC操作数据库添加事务操作

一、什么是事务

一个业务逻辑是一个最小的执行单元,这里面可能会有多个数据库操作。这些数据库操作要么都执行要么都执行。

业务逻辑:

一个业务就是需要完成的一件事情,是一个最小的执行单元,不可在拆分如:

A-B转账500:

A的账户钱减少500。 UPDATE acount SET money=money-500 WHERE name=‘A’

B的账户钱加500 UPDATE acount SET money=money+500 WHERE name=‘B’

事务的特征(ACID特性):

1、原子性 (atomicity)一个事务是不能拆分的最小执行单元。这个单元中的语句要么都执行,要么都不执行

2、一致性(consistency) 数据库中从一个一致性状态变为另一个一致性状。如:A 账户1000 B账户0元 总和1000 发生转账后 A账户500 B账户500 总和1000

3、隔离性 (isolation) 事务与事务之间是互相隔离,相互不影响

image-20210807153642193

4、持久性 (durability):一旦事务提交则数据会永久的保存(一旦事务提交则会永久保存在数据库中)

(持久化–就是永久的保存数据,持久化的手段:序列化,数据库,存文本…)

JDBC中对事务支持(涉及到数据的更新的时候 excuteUpdate)

1、关闭自动提交

conn.setAutoCommit(flase)

2、如果所有业务逻辑正常完成则提交事务

conn.commit()

3、如果发生异常则回滚事务

conn.rollback()

		//JDBC中做事务控制
		
		Connection conn = DBUtil.getConnection();
		
		//1.关闭数据库的自动提交
		try {
			conn.setAutoCommit(false);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		PreparedStatement ps1 = null;
		PreparedStatement ps2 = null;
		try {
			//TOM的账户钱少500
			String sql = "UPDATE acount SET money=money-500 WHERE uname='TOM'";
			//JERRY的账户钱加500
			String sql2 = "UPDATE acount SET money=money+500 WHERE uname='JERRY'";
			
			ps1 = conn.prepareStatement(sql);
			ps2 = conn.prepareStatement(sql2);
			
			int count1 = ps1.executeUpdate();
			//断电了
			//
			//int i =10/0;//模拟了程序执行是发生错误
			int count2 = ps2.executeUpdate();
			//事务第二步:如果都执行成功则提交事务
			conn.commit();
		} catch (SQLException e) {
			//如果发生异常进行事务的回滚(让它会到初始状态)
			try {
				conn.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
			try {
				ps1.close();
				ps2.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	

第三节:JDBC元数据获取

本节目标:

  • 获取数据库相关元数据信息
  • 获取数据表相关元数据信息

什么事元数据:

就是一些数据库相关的描述信息。

关于数据:数据库的版本,数据驱动名称…

关于ResultSet这张的表信息:多少列,多少行,每一列的列名

java.sql 接口 DatabaseMetaData封装数据库相关的信息

1、怎么得到对象

conn.getMetaData

2、哪个方法有作用


Connection conn = DBUtil.getConnection();
DatabaseMetaData metaData = conn.getMetaData();
//调用方法
System.out.println(metaData.getDriverName());
System.out.println(metaData.getDriverMajorVersion());
System.out.println(metaData.getDriverMinorVersion());
System.out.println(metaData.getURL());

Connection conn = DBUtil.getConnection();
String sql = "SELECT `name` as '姓名' FROM student";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
//得到对象  ResultSet
ResultSetMetaData metaData = rs.getMetaData();
//调用方法
//getColumnName(int column) 指定列的列名
/*System.out.println(metaData.getColumnName(2));
		System.out.println(metaData.getColumnCount());*/
for(int i = 1;i<=metaData.getColumnCount();i++) {
    System.out.println(metaData.getColumnName(i));
}
//获取列的JAVA的数据类型
System.out.println(metaData.getColumnClassName(1));
System.out.println(metaData.getColumnType(1));
//getColumnLabel(int column) 别名
System.out.println(metaData.getColumnLabel(1));

/getColumnName(int column) 指定列的列名
/System.out.println(metaData.getColumnName(2));
System.out.println(metaData.getColumnCount());
/
for(int i = 1;i<=metaData.getColumnCount();i++) {
System.out.println(metaData.getColumnName(i));
}
//获取列的JAVA的数据类型
System.out.println(metaData.getColumnClassName(1));
System.out.println(metaData.getColumnType(1));
//getColumnLabel(int column) 别名
System.out.println(metaData.getColumnLabel(1));


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值