JDBC、SQL注入、JDBC事务、工具类封装、悲观锁和乐观锁

JDBC

Java语言连接数据库

JDBC是SUN指定的一套接口(interface)。

JDBC编程六步

第一步:注册驱动
第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这是属于进程之间的通信,重量级的,是用完之后需要关闭)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行sql语句
第五步:处理查询结果集(只有当第四步执行的是select语句的时候才会处理查询结果集)
第六步:释放资源(使用完资源之后需要关闭资源,Java和数据库之间属于进程间的通信,开启之后需要关闭)

import java.sql.*;

public class jdbcTester {
    public static void main(String[] args) {
        try{
            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myemployee");

        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
}
import java.sql.*;

public class jdbcTester {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        Statement statement = null;
        try{
            //1、注册驱动
            Driver driver = new com.mysql.jdbc.Driver();
            DriverManager.registerDriver(driver);
            //2、获取连接
            String url = "jdbc:mysql://localhost:3306/myemployee";
            String use = "root";
            String password = "123";
            conn = DriverManager.getConnection(url,use,password);
            //3、获取数据库操作对象
            statement = conn.createStatement();
            //4、执行sql
            String sql = "insert into emp (name,salary) values (aa,1000)";
            //专门执行DML语句(insert,delete,update),返回值为影响数据库中的记录条数
            int count = statement.executeUpdate(sql);
            //5、处理查询结果集

        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            //6、释放资源
            try{
                if(conn!=null) {
                    conn.close();
                }
            }catch (SQLException e){
                e.printStackTrace();
            }
            try{
                if(statement!=null){
                    statement.close();
                }
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
    }
}

SQL注入

安全隐患

导致SQL注入的原因

//1、注册驱动
//数据库中的账户密码
String userName = "zfq";
String password = "123123";



Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://loaclhost:3306/myemployee","root","123");
//3、获取数据库操作对象
Statement statement = conn.createStatement();
/*
登录时实际输入的账号密码
zfq
zfq' or '1'='1
*/
//4、执行sql
String sql = "select * from t_user where userName = '"+userName+"' and password = '"+password+"'";
ResultSet rs = statement.executeQuery(sql);//此时将sql发送给DBMS(数据库管理系统)进行编译。
//完成sql语句的拼接,正好将用户提供的‘非法信息’编译进去,导致原sql语句的含义被扭曲,进而实现了sql注入
#此时执行的sql语句
select * from t_user where userName = 'zfq' and password = 'zfq' or '1'='1';

输入的密码被当做sql语句的一部分后被执行
用户输入的信息中心含有sql语句的关键字,这些关键字参与了sql语句的编译过程

解决sql注入

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

想要用户信息不参与SQL语句的编译过程,就必须使用java.sql.PreparedStatement接口

PreparedStatement接口

继承了Statement接口;

原理:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”;

public interface PreparedStatement extends Statement

PreparedStatement:预编译的数据库操作对象
Statement:数据库操作对象

//数据库中的账户密码
String userName = "zfq";
String password = "123123";


//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://loaclhost:3306/myemployee","root","123");
//3、获取预编译的数据库操作对象
//先写sql语句的框架,其中一个?代表一个占位符,每个?接受一个值,不能加单引号。
String sql = "select * from t_user where userName = ? and password = ?";
//程序执行到此,会发送SQL语句框架给DBMS,然后DBMS对SQL语句预先编译。
PreparedStatement ps = conn.prepareStatement(sql);
//给占位符?传值(第一个问号下标为1,第二个问号下标为2……)
ps.setString(1,userName);
ps.setString(2,newPassword);
ResultSet rs = statement.executeQuery();//执行

PreparedStatement与Statement

Statement存在SQL注入的问题,Preparedstatement解决了SQL注入的问题。
Statement编译一次执行一次,PreparedStatement编译一次执行多次,PreparedStatement效率高一些。
PreparedStatement会在编译阶段做类型的安全检查。

PreparedStatement使用较多,Statement只在极少数情况下才会使用。
Statement支持SQL注入,当需要进行SQL语句拼接时就需要使用Statement。

PreparedStatement实现增删改

Connection conn = null;
PreparedStatement ps = null;
try{
	//注册驱动
	Class.forName("com.mysql.jdbc.Driver");
	//获取连接
	conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myemployee","root","123");
	//获取预编译的数据库操作对象
	
	//#######insert######
	String sql = "insert into emp (name,salary) values (?,?)";
	ps = conn.prepareStatement(sql);
	ps.setString(1,"sss");
	ps.setInt(2,12000);
	//#######insert######

	
	//#######update######
	String sql = "update emp set salary = ? where name = ?";
	ps = conn.prepareStatement(sql);
	ps.setInt(1,12000);
	ps.setString(2,"sss");
	//#######update######


	//#######delete######
	String sql = "delete form emp where name = ?";
	ps = conn.prepareStatement(sql);
	ps.setString(1,"sss");
	//#######delete######



	//执行sql语句
	int count = ps.executeUpdate();
	System.ouot.println(count);
}catch(Exception e){
	e.printStackTrace();
}finally{
	try{
		if(ps != null){
			ps.close();
		}
	}catch(SQLException e){
		e.printStackTrace();
	}
	try{
		if(conn != null){
			conn.close();
		}
	}catch(SQLException e){
		e.printStackTrace();
	}
}

JDBC事务机制

JDBC中的事务默认是自动提交的——只要执行一条DML语句就会自动提交一次。

connection.setAutoCommit(boolean)

验证自动提交

public class JDBC{
	public static void main(String[] args){
		Connection conn = null;
		PrepareStatement ps = null;
		try{
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myemployee","root","123");
			String sql = "delete from emp where name = ?";
			ps = conn.prepareStatement(sql);
			ps.setString("111");
			int count = ps.executeUpdate();
			System.out.println(count);
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try{
				if(ps!=null){
					ps.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
			try{
				if(conn!=null){
					conn.close();	
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
}

手动提交

idea批量编辑:alt+shift+insert

//三行代码
conn.setAutoCommit(false);//关闭自动提交
conn.commit();//手动提交
conn.rollback();//抛出异常后需要回滚
#建一个新的表
drop table if exists t_act;
create table t_act(
	actno bigint,
	balance double(7,2) #7表示有效数字的个数,2表示小数位的个数
);
insert into t_act(actno,balance) values (111,20000);
insert into t_act(actno,balance) values (222,20000);
public class JDBC{
	public static void main(String[] args){
		Connection conn = null;
		PrepareStatement ps = null;
		try{
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myemployee","root","123");
			//开启手动提交
			conn.setAutoCommit(false);
			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);
			
			//程序能执行到这里说明没有异常,可以提交
			//提交commit事务
			conn.commit();
		}catch(Exception e){
			//如果发生异常则进行回滚	
			if(conn != null){
				conn.rollback();
			}
			e.printStackTrace();
		}finally{
			try{
				if(ps!=null){
					ps.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
			try{
				if(conn!=null){
					conn.close();	
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
}
}

JDBC工具类的封装

工具类中的构造方法都是私有的。

public class DBUtil{

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

	//获取数据库连接对象
	public static Connection getConnection() throws SQLException{
		return DriverManager.getConnection("jdbc:mysql://localhost:3306/myemployee","root","123");
	}
	
	public static void close(Connection conn,Statement ps,ResultSet rs){
		try{
			if(rs!=null){
				rs.close();
			}
		}catch(SQLException e){
			e.printStackTrace();
		}
		try{
			if(ps!=null){
				ps.close();
			}
		}catch(SQLException e){
			e.printStackTrace();
		}
		try{
			if(conn!=null){
				conn.close();	
			}
		}catch(SQLException e){
			e.printStackTrace();
		}


	}
}

悲观锁和乐观锁

乐观锁:低并发,认为就算是在并发的情况下,也很少修改数据。
悲观锁:高并发,认为修改数据很频繁,不值得去进行无用的计算。(执行效率慢)

行级锁(悲观锁)

事务必须排队,数据被锁住,不能并发。
for update

#普通
select name from emp where job = 'manager';
#行级锁
select name from emp where job = 'manager' for update;

表示当前事务没有结束的时候,满足 job = ‘manager’ 的每一行的数据都不能改动。

乐观锁

事务不需要排队,支持并发,只不过需要一个版本号。

多线程并发,都可以对数据进行修改。但是每行数据后面有一个版本号。
当一个线程修改了这一行数据之后,版本号进行更新;当另一个线程修改完这行数据后准备提交时发现版本号变了,这个线程就会进行回滚。

演示

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值