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) 事务与事务之间是互相隔离,相互不影响
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));