【JDBC】JDBC、JdbcUtil工具类源码、sql注入问题、事务、数据库连接池(DBCP)

一、什么是jdbc?

数据库驱动

将应用程序与数据库之间建立连接

jdbc:

为了简化开发人员的同意操作,提出了java操作数据库的规范,规范由具体的厂商去完成,开发人员只需要掌握jdbc的接口。

需要的包

javax.sql
com.mysql.jdbc.Driver(数据库驱动包)


二、连接数据库步骤:

1.加载驱动

class.forName(com.mysql.jdbc.Driver);


2.用户信息和url

//useUnicode=true 识别中文
//characterEncoding=utf8 字符集
//useSSL=true 安全连接
String url = "jdbc:mysql://local:3306/数据库名?+"
+"useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String passwd = "";


3.连接成功,数据库对象

//con 代表数据库;
Connection con = DriverManager.getConnection(url,root.passwd);


4.执行SQL的对象

//statement :执行SQL的对象
Statement statement = con.createStatement();
preperStatement statement = con.preperStatement(); //预处理


5.执行SQL的对象 去执行SQL,可能有返回值

String sql = "";
ResultSet rs = statement.executeQuery(sql);//查询
int rs = statement.executeUpdata(sql); //增删改
statement.execute(sql)//增删改查
//获取信息
whiel(rs.next()){
	String name = rs.getString("字段名");
}


6.关闭连接

rs.close();
statement.close();
con.close();
JdbcUtil工具类源码:
package sdpei.jsj.comment;

import java.sql.*;

/**
 * @Description 这个类是作为对数据库操作(增删改查)操作的标准工具类
 * @author HuXuehao
 */
public class JdbcUtil {
	private static final String URL = "jdbc:mysql://localhost:3306/gym?useSSL=false&serverTimezone=UTC";
	private static final String USER = "root";
	private static final String PASSWORD = "123123";
	protected static Statement s = null; // 发送语句用的
	protected static ResultSet rs = null; // 接收查询的结果集用的
	/*
	 * 这里我还是解释一下ThreadLocal的是什么, 怎使用吧
	 * 算了,我还是给链接吧:https://www.cnblogs.com/dreamroute/p/5034726.html
	 * 
	 */
	protected static ThreadLocal<Connection> tl = new ThreadLocal<>();

	/**
	 * @Description 建立与数据 库的链接
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月16日下午6:03:54
	 * @return Connection 对象
	 */
	private static synchronized Connection getConnection() {
		Connection conn  = tl.get();
		if (conn != null) // 这里的目的是为了让事务中的数据操作是使用的conn和开启事务的conn是同一个
			return conn; 
		try {
			Class.forName("com.mysql.cj.jdbc.Driver"); // 加载com.mysql.jdbc.Driver这个驱动类
			conn = DriverManager.getConnection(URL, USER, PASSWORD); // 连接数据库
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn; // 连接成功后的对象
	}

	/**
	 * @Description 对数据库进行插入、更新、删除操作(只适合相对静态SQL语句,
	 *              其实我们也可以换一种方式实现动态语句,比如在java中我们可以这样做: 
	 *              int userId = 10; 
	 *              String sql = "select * from table where id="+userId;
	 *              上述的这种sql传到executeUpdate(String sql)是不可以改变的,但是但是在传之前userId是可以
	 *              变化的。)
	 *              
	 * 注意:需要手动调用该类中的close()方法
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月16日下午6:05:08
	 * @param sql
	 * @return 操作成功就返回影响的行数,操作不成功就返回0
	 */
	public static int executeUpdate(String sql) {
		int result = 0;
		try {
			s = getConnection().createStatement(); // 通过conn这个对象获取Statement对象s,s用来发送SQL语句
			result = s.executeUpdate(sql); // 执行增删改语句,使用Statement是有参数的
		} catch (SQLException e) {

			e.printStackTrace();
		}
		return result;
	}

	/**
	 * @Description 对数据库进行查询操作(同上)
	 * 注意:需要手动调用该类中的close()方法
	 * 
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月16日下午6:05:38
	 * @param sql
	 * @return Result结果集
	 */
	public static ResultSet executeQuery(String sql) {
		try {
			s = getConnection().createStatement();
			rs = s.executeQuery(sql); // 执行查询语句
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}

	/**
	 * @Description 执行动态的SQL语句 之所以可以实现动态语句,是因为它的参数中的SQL语句是要带“?”的 
	 * 例如: 
	 * String sql="insert into information(userName,userType)"+ "values(?,?)";
	 * PreparedStatement ps=null; 
	 * ps.executePreparedStatement(String sql) //这里面是有参数的 ps.setString(1,"huxuehao"); ps.setString(2,18);
	 * int result = ps.executeUpdate(); //这里面是没有参数的
	 * 
	 * 注意:需要手动调用该类中的close()方法
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月16日下午6:12:07
	 * @param sql
	 * @return preparedStatement对象
	 */
	public static PreparedStatement executePreparedStatement(String sql) {
		PreparedStatement ps = null;
		try {
			ps = getConnection().prepareStatement(sql); // 只是获取到了发送动态语句的对象ps
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ps;
	}

	/**
	 * @Description 关闭数据库连接对象
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月16日下午6:06:21
	 */
	public static void close() {
		Connection conn = tl.get();
		try {
			if (rs != null)
				rs.close();
			if (s != null)
				s.close();
			if (conn != null)
				conn.close();

		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
//--------------------------------------事务的手动提交以及事务的回滚----------------------------------------------
	/**
	 * 下面是开启事务,事务提交,事务回滚的三个方法 
	 * 用法: 
	 * try{ 
	 * 		JdbcUtil.beginTransaction() //开启事务
	 *		 对数据库的操作..... 
	 * 		JdbcUtil.commitTransaction() //提交事务 
	 * }catch{
	 * 		JdbcUtil.rollbackTransaction() //回滚事务 
	 * }
	 * 上面的 getConnection()方法中有一判断conn是否不为空,当conn不为空时,直接返回已经创建的链接conn,保证对数据库的操作是使用
	 * 的是同一个conn,即保证在同一个事务中
	 * 
	 * 注意:存在一个致命的问题,就是我在开启事务调用增删改查方法后,他就把conn给关了,那么我后面的提交事务和回滚事务就是无效操作
	 * 	         所以我在上面的方法中并没有调用上面定义的close()方法,这就意味着当你没有创建事务但是调用了操作数据库的方法时,我们需要在手动调用该工具类中的close()方法
	 * @Description 开启事务
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @return
	 * @date 2020年10月29日下午1:43:13
	 */
	public static void beginTransaction() {
		Connection conn = tl.get(); //获取自己的链接,接下来判断其是否为空
		if (conn == null) throw new RuntimeException("你说你是不是手欠,已经开启了事务,咱就不要重复操作了");
		conn = getConnection(); //创建连接
		try {
			conn.setAutoCommit(false); // 事务设置为手动提交
			tl.set(conn); //把当前线程的链接保存起来,为了给下面的使用
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @Description 事务的提交
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月29日下午1:43:33
	 */
	public static void commitTransaction() {
		Connection conn = tl.get();
		if (conn == null) throw new RuntimeException("还没开启事务呢,你找啥急去提交事务");
		try {
			conn.commit(); //提交事务
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close();
				tl.remove(); //移除当前连接
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @Description 事务的回滚
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月29日下午1:43:56
	 */
	public static void rollbackTransaction() {
		Connection conn = tl.get();
		if (conn == null) throw new RuntimeException("还没开启事务呢,你找啥急去回滚事务");
		try {
			conn.rollback(); //回滚事务
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				conn.close();
				tl.remove(); //移除当前连接
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @Description 事务回滚(弃用)
	 * @author HuXuehao Email:1938667362@qq.com
	 * @version
	 * @date 2020年10月16日下午6:06:08
	 */
	/*
	 * public static void rollback() { try { getConnection().rollback(); //回滚 }
	 * catch (SQLException e) { e.printStackTrace(); } }
	 */

}


三、sql注入问题:

SQL存在漏洞,导致会被攻击 (sql存在拼接 [or])

通过SQL语句,实现无账号登录,甚至篡改数据库。

举例:
加入后台的SQL语句如下:

String sql = “select * from user where username=’ “+userName+” ’ and password=’”+password+" '";

用户输入内容如下:

userName输入: or 1=1 –
password输入:00000 (随便输入)

那么原本的SQL语句就变成了:

select * from user where username=’’ or 1=1 - - and password =‘00000’
该语句很显然永远成立
username=’’ or 1=1为真,- - and password ='00000’很明显是一条注释


prepareStatement对象(预处理)可以防止SQL注入并且效率更高

//本质:将传递进来的参数当做字符,其中存在引号,则会转译
PrepareStatement pst = con.prepareStatement("....?,?,?");
pst.setInt(1,1); //第一1代表上面的第一个“?”,第二个1代表当前“?"的具体内容
pst.setString(2,""); //同上
pst.setData(3, java.sql.Data(new Data().getTime())); //同上
pst.executeQuery(); //执行查询操作
pst.executeUpdate(); //执行增、删、改操作

//释放资源.
con.close(); 
pst.close();



四、事务

作用:

在开启事务和提交事务期间程序发生异常,那么该期间的所有SQL执行都会被回滚

ACID原则:

原子性:要么都完成,要么都不完成
一致性:总数不变
隔离性:多进行互不干预
持久性:一单提交,不可回滚


隔离性的问题:

脏读:一个事务读取了另一个没有提交的数据
不可重复读:在一个事务内,重复读取表中的数据,表数据发生了改变
虚读(玄读):在一个事务内,读取到别人插入的数据,导致前后读取出来的数据不一致

例子

------------------【转账】------------------

前提:假设已经连接好了获取了con对象

try{
	//1. 关闭自动提交,并开启事务
	con.setAutoCommit(false)

	2.其他SQL操作.....	

	//3.提交事务
	con.commit() 
}catch(Exception e){
	try{
		//4.回滚
		con.rollback();
	}catch(Exception e){
		e.printStackTrance();
	}
	
}finally{
	关闭资源
}


五、数据库连接池

池化技术:

预先准备一些资源,需要就直接用。
存在【最大连接数】
存在【最小连接数】
存在【等待超时】

编写连接池:

本质上就是实现接口

常用的开源数据源

1、DBCP
2、C3P0
3、Druid(阿里巴巴)



【DBCP的使用】(需要commons-dacp.jar 和 commons-pool.jar)
在这里插入图片描述

dbcp.properties文件

########DBCP配置文件##########
#驱动名
driverClassName=com.mysql.jdbc.Driver
#url
url=jdbc:mysql://local:3306/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=true
#用户名
username=user
#密码
password=123456
#初试连接数
initialSize=50
#最大活跃数
maxTotal=35
#最大idle数
maxIdle=20
#最小idle数
minIdle=10
#最长等待时间(毫秒)
maxWaitMillis=1000
#程序中的连接不使用后是否被连接池回收(该版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#连接在所指定的秒数内未使用才会被删除(秒)(为配合测试程序才配置为1秒)
removeAbandonedTimeout=1


java代码:

//1.通过类加载器进行获取properties文件流
InputStream  in = 共有类名.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
//2.创建Properties类
Properties properties = new Properties();
//3.加载流文件
properties.load(in);
//4.创建数据源(工程模式———>创建)
DataSource datasource = BasicDataSourceFactory.createDataSource(properties);
//5.创建连接
Connection = con datasource.getConnection()
//连接已经穿件好了,剩下的就是增删改查操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值