JDBC连接 防止注入 事件 回滚

来源:互联网 发布:淘宝客推广平台有哪些 编辑:程序博客网 时间:2024/06/05 05:34
1.

JDBC 是一套接口(规范) 那各个厂商想要让java语言去操作他们的数据库,必须实现这套接口
厂商写的一套实现类 我们叫做 数据库驱动
 
import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;public class JDBCtest {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.导入数据数据库包 jar包//2 加载驱动jar包Class.forName("com.mysql.jdbc.Driver");//3 获取一个数据库连接对象         参数1:数据库的路径 参数2,3 数据库的用户名和密码String  url = "jdbc:mysql:///mydb_01";String user = "root";String password = "root";Connection conn = DriverManager.getConnection(url, user, password);//4 通过连接对象获取操作对象Statement statement = conn.createStatement();//用于执行静态 SQL 语句并返回它所生成结果的对象。      // statement是一个接口  //5定义sql语句String sql = "insert into tescher values(3,'rrr','333')";//6执行语句int i = statement.executeUpdate(sql);//返回此语句改变的行数if(i!=0){System.out.println("成功");}else{System.out.println("失败");}//7释放资源conn.close();statement.close();}}


2.
获取Connection对象:
Connection conn = DriverManager.getConnection(url,user,password);
url:访问数据库的路径
jdbc:     mysql://    192.168.3.100:     3306     /day
              主协议    子协议 ip地址      端口号  数据库名称

* 细节:如果连接的是本地的数据库,则ip和端口可以省略
jdbc:mysql:///day04
3.
Connection:数据库连接对象
* 创建执行sql的对象
* createStatement:创建Statement对象
* prepareStatement:创建preparedStatement对象
* 可以防止sql注入,提高执行效率
* prepareCall: 创建preparedCall对象
* 执行存储过程的
4.
Statement:执行sql的对象

* executeUpdate(sql):执行DML语句  (增、删、改)
* 返回值:int,代表影响的行数
* executeQuery(sql):执行DQL语句 (查询)
* 返回值:ResultSet对象  结果集对象,对结果集的封装

* execute(sql):执行任意语句
       * 返回值:boolean,执行成功与否
5.
 PreparedStatement:执行sql的对象
* 登陆:
如果使用statement,可能发生sql注入。

* 功能:
* 预防sql注入

* 使用方式:
1.定义sql时,所有的参数位置 需要 使用 ? 作为占位符
2.Connection对象获取执行sql 对象 prepareStatement(sql)
3.给占位符 ? 赋值
* setXxx(参数1,参数2):
* 参数1:?出现的位置 从1开始
* 参数2:?对应的值
4.执行sql时,使用不带参数的方法
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;




6.预编译  查询表  抽取工具类

import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBC03 {public static void main(String[] args) throws ClassNotFoundException, SQLException {//拷贝JAR包//加载驱动//Class.forName("com.mysql.jdbc.Driver");//获取连接对象//Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb_01", "root", "root");//通过连接对象获取预编译操作对象Connection conn = JDBCutils.getConnection();String sql  = "select * from tescher where id = ?";PreparedStatement statement = conn.prepareStatement(sql);statement.setInt(1, 3);// 给问号赋值   第一个参数:id位于表中的第几个参数 第二个参数:?的值    Int表示?的类型//执行sql语句ResultSet resultSet = statement.executeQuery();while(resultSet.next()){//如果查到了 光标将发生跳转  返回值变为trueint id = resultSet.getInt(1);String name = resultSet.getString(2);String sex = resultSet.getString(3);System.out.println(id+name+sex);}//conn.close();//statement.close();//resultSet.close();JDBCutils.close(conn, statement, resultSet);}}

工具类
import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBCutils {//全局变量 public static String url; public static String user; public static String password;  //静态代码块static{ url  = "jdbc:mysql:///mydb_01"; user = "root"; password = "root";try {Class.forName("com.mysql.jdbc.Driver");} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}//不让外界创建对象  私有构造(外界只能通过类名.方法名  来使用)private JDBCutils(){super();}//返回连接对象public static Connection getConnection() throws SQLException{return DriverManager.getConnection(url, user, password);}//关闭资源public static void close(Connection conn ,Statement statement , ResultSet result ) throws SQLException{if(conn != null){conn.close();}if(statement!= null){statement.close();}if(result!= null){result.close();}}//要是有的没有用到结果集(ResultSet) 这时候就需要方法重载public static void close(Connection conn, Statement statement) throws SQLException{if(conn!=null){conn.close();}if(statement!=null){statement.close();}}}




7. 
读取表中的数据

import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;public class JDBC02 {public static void main(String[] args) throws ClassNotFoundException, SQLException {//导入驱动JAr包//加载驱动Class.forName("com.mysql.jdbc.Driver");//获取链接对象Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb_01", "root", "root");//通过连接对象获取操作对象Statement statement = conn.createStatement();//定义sql语句String sql = "select * from tescher";//executeUpdate(sql):执行DML语句  (增、删、改)  返回值:int,代表影响的行数                //executeQuery(sql):执行DQL语句 (查询)  返回值:ResultSet对象  结果集对象,对结果集的封装                //execute(sql):执行任意语句        返回值:boolean,执行成功与否//执行sql语句ResultSet resultSet = statement.executeQuery(sql);//创建集合来存储读取到的数据 然后输出遍历ArrayList<User> list = new ArrayList<User>();//boolean b = resultSet.next();//返回值是用来判断是否还有下一行while(resultSet.next()){//在结果集里面 按照顺序下移一行int id = resultSet.getInt(1);String name = resultSet.getString(2);String sex = resultSet.getString(3);User user = new User(id,name,sex);//从数据库里面取出数据 是想使用这些数据 先封装到对象 再把每个对象 装到集合里list.add(user);}//遍历集合System.out.println(list);//释放资源conn.close();statement.close();resultSet.close();}}


8.防止sql注入   采取预编译  

import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Scanner;public class JDBC04 {public static void main(String[] args) throws ClassNotFoundException, SQLException {Scanner sc = new Scanner(System.in);System.out.println("请输入姓名");String username = sc.next();System.out.println("请输入密码");String password = sc.next();Class.forName("com.mysql.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb_01", "root", "root");//定义sql语句 通配符?代替数据String sql = "select * from 登陆表格 where username = ? and password = ? ";//获取预编译操作对象PreparedStatement statement = conn.prepareStatement(sql);//给通配符赋值   参数1为?在表中的位置  参数2为?的值statement.setString(1, username);statement.setString(2, password);//执行sql语句ResultSet resultSet = statement.executeQuery();if(resultSet.next()){System.out.println("登陆成功");}else{System.out.println("登陆失败");}//释放资源resultSet.close();statement.close();conn.close();}}


9.事件(以银行转账为例)

import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import JDBC03.JDBCutils;//zs给ls转账100public class jdbc05 {public static void main(String[] args) throws Exception {//获取连接对象Connection conn = JDBCutils.getConnection();//定义sql语句String sql1 = "update bank set money=money-100 where username = ?";//转出String sql2 = "update bank set money=money+100 where username = ?";//转入//获取预编译对象PreparedStatement statement1 = conn.prepareStatement(sql1);PreparedStatement statement2 = conn.prepareStatement(sql2);//给?赋值  参数1为username在表中的位置  参数2为?的值statement1.setString(1, "zs");statement2.setString(1, "ls");//执行sql语句statement1.execute();statement2.execute();//释放资源JDBCutils.close(conn, statement1);JDBCutils.close(conn, statement2);}}

现在制造一个异常来模拟:当张三将钱转出去,服务器发生异常,强制终止

             
   //执行sql语句statement1.execute();//人为加一个异常System.out.println(1/0);statement2.execute();


这时候会发现zs转账过去了 但ls并没有收到 这时引入事件


基本概念:
      事务使指一组最小逻辑操作单元,里面有多个操作组成。 组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。


事务ACID特性
原子性(Atomicity)
          原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 
一致性(Consistency)
          事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation)
          事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
          持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响


事务的特性:
  原子性,是一个最小逻辑操作单元 !
  一致性,事务过程中,数据处于一致状态。                                                      
  持久性, 事务一旦提交成功,对数据的更改会反映到数据库中。
  隔离性, 事务与事务之间是隔离的。




针对上述服务器异常问题  就需要回滚来解决
        Connection SetAutoCommit(boolean b):开启事物,默认为true,b=false则开启事物
        Connection rollback():回滚事物到初始状态(当程序运行异常,张三李四账户余额不变 所以是在异常处抓一下)
        Connection commit():提交事物

import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import JDBC03.JDBCutils;//zs给ls转账100public class jdbc05 {public static void main(String[] args) throws Exception {//建立连接Connection conn = JDBCutils.getConnection();//开启事务conn.setAutoCommit(false);//定义sql语句String sql1 = "update bank set money=money-100 where username = ?";//转出String sql2 = "update bank set money=money+100 where username = ?";//转入//获取预编译对象PreparedStatement statement1 = conn.prepareStatement(sql1);PreparedStatement statement2 = conn.prepareStatement(sql2);//给?赋值  参数1为username在表中的位置  参数2为?的值statement1.setString(1, "zs");statement2.setString(1, "ls");try {//执行sql语句statement1.execute();//人为加一个异常System.out.println(1/0);statement2.execute();} catch (Exception e) {//一旦发生异常 回滚到最初状态conn.rollback();}finally{//提交事务conn.commit();}//释放资源JDBCutils.close(conn, statement1);JDBCutils.close(conn, statement2);}}


当服务器发生异常 数据会回滚到最原始


10.事物回滚点


事物回滚点 当程序中执行两次转账,第一次成功,第二次失败,回滚到初始状态 但我想第一转账成功保留,只回滚到第二次转账前的状态,怎么办?
 需要设置一个回滚点 Connection setSavepoint():
 在两次之间设置一个回滚点 Connection rollback(Connection setSavepoint()):回滚到回滚点上(即回滚到第一次转账结束后)


import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Savepoint;import JDBC03.JDBCutils;//zs给ls转账100public class jdbc05 {public static void main(String[] args) throws Exception {Savepoint savepoint = null;//建立连接Connection conn = JDBCutils.getConnection();//开启事务conn.setAutoCommit(false);//定义sql语句String sql1 = "update bank set money=money-100 where username = ?";//转出String sql2 = "update bank set money=money+100 where username = ?";//转入//获取预编译对象PreparedStatement statement1 = conn.prepareStatement(sql1);PreparedStatement statement2 = conn.prepareStatement(sql2);//给?赋值  参数1为username在表中的位置  参数2为?的值statement1.setString(1, "zs");statement2.setString(1, "ls");try {//执行sql语句//第一次转账(正常)statement1.execute();statement2.execute();//在第一次和第二次转账之间设置回滚点 savepoint = conn.setSavepoint();//第二次转账(异常)statement1.execute();//人为加一个异常System.out.println(1/0);statement2.execute();} catch (Exception e) {//一旦发生异常 回滚到设置的回滚点conn.rollback(savepoint);}finally{//提交事务conn.commit();}//释放资源JDBCutils.close(conn, statement1);JDBCutils.close(conn, statement2);}}