Java-jdbc预编译对象,事务,数据库连接池,DButil工具类的书写

4 篇文章 0 订阅

jdbc预编译对象

原始的jdbc使用声明对象进行sql的执行,对于执行的sql参数使用字符串拼接的形式进行添加

	Class.forName("com.mysql.jdbc.Driver");
		Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/teacher", "root", "root");
		Statement statement = con.createStatement();
		// 当使用原始的jdbc进行相应操作时,由前台获取相应查询参数,保存至变量。准备sql时将参数
		// 使用字符串拼接的方式进行数据的拼接,这有时会导致一些问题
		// 如果使用者知道并了解sql的语句,那么就可以使用sql注入的方式混乱查询结果
		// 从而导致系统的不稳定
		// 将id与name当做账号密码进行登录操作
		String studentid = "203213213";
		String studentname = "231313' or studentname!='1";
		// 登录的sql语句就会变为
		String sql = "select * from student where studentid=" + studentid + " and studentname= '" + studentname+"'";
		//select * from student where studentid=203213213 and studentname= '231313' or studentname!='1'
		//输入者可以猜测数据库sql语句并将sql以数据的形式传入
		//如果使用字符串拼接的形式进行sql的生成那么会导致结果不符
		ResultSet rs = statement.executeQuery(sql);
		ArrayList<Student> list = new ArrayList<>();
		while (rs.next()) {// 如果结果集对象中存在数据
			Student student = new Student(rs.getString("studentid"), rs.getString("studentname"),
					rs.getInt("studentage"), rs.getString("studentsex"), rs.getString("studentaddress"),
					rs.getInt("classid"));
			list.add(student);
		}
		con.close();
		for (Student student : list) {
			System.out.println(student);
		}

为了解决sql注入的问题,需要使用sql预编译对象

	Class.forName("com.mysql.jdbc.Driver");
		Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/teacher", "root", "root");
		String studentid = "203213213";
		String studentname = "231313' or studentname!='1";
		// String sql = "select * from student where studentid=" + studentid + "
		// and studentname= '" + studentname + "'";

		// 原始的jdbc先创建声明对象在执行时才将sql交由声明对象执行
		// Statement statement = con.createStatement();
		// ResultSet rs = statement.executeQuery(sql);

		// 预编译对象是先准备执行的sql 将所有参数使用?占位符替换
		String sql = "select * from student where studentid= ? and studentname= ?";
		// 使用连接对象获取预编译对象并将sql进行预编译
		PreparedStatement ps = con.prepareStatement(sql);
		// 为占位符进行赋值
		ps.setObject(1, studentid);// 为指定占位符赋值
		ps.setObject(2, studentname);

		ResultSet rs = ps.executeQuery();
		ArrayList<Student> list = new ArrayList<>();
		while (rs.next()) {// 如果结果集对象中存在数据
			Student student = new Student(rs.getString("studentid"), rs.getString("studentname"),
					rs.getInt("studentage"), rs.getString("studentsex"), rs.getString("studentaddress"),
					rs.getInt("classid"));
			list.add(student);
		}
		con.close();
		for (Student student : list) {
			System.out.println(student);
		}

事务

在进行数据库操作时,为了保证数据的安全以及减少错误的发生,一般都会进行事务的管理,数据库在进行操作时,内部也进行了简单的事务管理,只不过在进行更复杂操作时,这种默认的事务不足以进行相应需求的实现,这个时候就需要我们手动的进行事务的管理

事务的ACID原则

· ***事务的原子性( Atomicity):***一组事务,要么成功;要么撤回。

· ***一致性 (Consistency):***事务执行后,数据库状态与其他业务规则保持一致。如转账业务,无论事务执行成功否,参与转账的两个账号余额之和应该是不变的。

· ***隔离性(Isolation):***事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度。

· ***持久性(Durability):***软、硬件崩溃后,InnoDB数据表驱动会利用日志文件重构修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 选项 决定什么时候吧事务保存到日志里。

mysql数据库中 使用sql语句进行事务处理

#在执行多条sql前手动开+启事务
start transaction;
update student set studentname='Bill' where studentid='2010005';
update student set studentage=88 where studentid='2010005';

#在sql执行结束后手动提交事务
commit;

#mysql中如果事务在执行过程中发生异常导致事务执行出现问题,会自动进行回滚
#将已经执行的更改进行撤销
rollback;

jdbc中进行事务处理

jdbc进行事务处理的语法

获取连接对象con

设置con.setAutoCommit(false);//关闭事务的自动提交-》开启事务

执行多条sql

对执行sql语句进行捕获如果出现异常则con.rollback();回滚事务

执行成功则 con.commit(); 提交事务

// mysql默认开启事务的自动提交
		// 每执行1条SQL语句都是一个事务
		Class.forName("com.mysql.jdbc.Driver");
		Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/teacher", "root", "root");
		try {
			// 默认事务自动提交 每执行1条sql一个事务 其他事务不会影响该事务的执行
			String sql = "update student set studentname='Bill' where studentid='2010005' ";
			// 事务的开启
			// 在执行sql之前设置当前连接
			// 将自动提交关闭
			con.setAutoCommit(false);

			PreparedStatement ps = con.prepareStatement(sql);
			ps.executeUpdate();
			// 第二条sql语句报错没有执行成功 但是第一条数据已经执行成功存入数据库
			String sql2 = "update student set studesntage=28 where studentid='2010005' ";
			PreparedStatement ps2 = con.prepareStatement(sql2);
			ps2.executeUpdate();
		} catch (Exception e) {
			// 对执行的sql语句进程try catch 如果出现异常回滚事务
			con.rollback();
		}
		// 如果执行成功则提交事务将执行的结果保存
		con.commit();

事务的隔离级别

基于事务的隔离性,事务直接独立互不影响,但也是取决于对不同数据的操作,如果多个事务对同一个数据进行操作可能造成类似于java多线程操作同一变量的问题,从而导致数据的不准确

因为并发事务导致的问题大致有5类,其中两类是更新问题三类是读问题。

· 脏读(dirty read):读到另一个事务的未提交新数据,即读取到了脏数据;

· 不可重复读(unrepeatable):对同一记录的两次读取不一致,因为另一事务对该记录做了修改;

· 幻读(虚读)(phantom read):对同一张表的两次查询不一致,因为另一事务插入了一条记录。

四大隔离级别

4个等级的事务隔离级别,在相同的数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。

1、SERIALIZABLE(串行化)

所有事务串行依次执行,不会发生任何数据问题,但是效率最低

2、REPEATABLE READ(可重复读)(MySQL)

mysql默认的事务隔离级别,事务之间在进行操作时,对操作的数据进行限制,不允许其他事务对当前事务正在处理的数据(行)进行操作,可以避免脏读和不可重复读,效率比串行化高一点

3、READ COMMITTED(读已提交数据)(Oracle)

oracle数据库默认的事务隔离级别,多个事务进行执行,除了可以任意读取操作其他事务没有处理的数据外,只能读取其他事务已经提交了的数据,可以防止脏读,但是不能防止不可重复读与幻读,效率比可重复读高一点

4、READ UNCOMMITTED(读未提交数据)

不进行任何处理,不能解决任何隔离问题,但效率最高

mysql设置隔离级别

MySQL的默认隔离级别为Repeatable read,可以通过下面语句查看:

SELECT @@TX_ISOLATION;

也可以通过下面语句来设置当前连接的隔离级别:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ;//[4选1]

JDBC设置隔离级别

con.setTransactionIsolation(int level) :参数可选值如下:

· Connection.TRANSACTION_READ_UNCOMMITTED;

· Connection.TRANSACTION_READ_COMMITTED;

· Connection.TRANSACTION_REPEATABLE_READ;

· Connection.TRANSACTION_READ_SERIALIZABLE。

c3p0数据库连接池的使用

数据库连接池:通过配置在程序执行时直接创建多个数据库连接并进行保存,当用户需要使用连接时调用相应方法获取连接,连接的声生命周期交由连接池控制。

将与数据库的连接交由连接池进行管理,我们只需要在使用时获取连接进行使用,之后连接池会自动回收相应的连接

1、导入c3p0的jar包

注意:c3p0额外依赖另一个jar包 所以在导入时需要导入两个jar包

2、书写连接池配置文件

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
	<!-- 这是默认配置信息 -->
	<default-config>
		<!-- 连接四大参数配置 -->
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/teacher?characterEncoding=UTF-8</property>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="user">root</property>
		<property name="password">root</property>
		<!-- 池参数配置 -->
		<!-- 如果池中数据连接不够时一次增长多少个 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化数据库连接池时连接的数量 -->
        <property name="initialPoolSize">20</property>
        <!-- 数据库连接池中的最大的数据库连接数 -->
        <property name="maxPoolSize">25</property>
        <!-- 数据库连接池中的最小的数据库连接数 -->
        <property name="minPoolSize">5</property>

	</default-config>
</c3p0-config>

3、创建c3p0对象进行使用

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Test {
	final static ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
	// 使用c3p0数据库连接池获取连接
	public static void main(String[] args) throws SQLException {
		//C3P0获取连接代码(默认获取src下c3p0-config.xml)
		Connection connection = comboPooledDataSource.getConnection();
		String sql="select * from student";
		PreparedStatement ps = connection.prepareStatement(sql);
		ResultSet rs = ps.executeQuery();
		while(rs.next()){
			System.out.println(rs.getString(1)+"|"+rs.getString(2));
		} 	 
	}
}

JDBC工具类的集成

		//根据预编译对象预编译的sql语句获取结果元数据
		//包含返回数据所有列的相应信息(没有行)
		ResultSetMetaData metaData = ps.getMetaData();
		//获取返回数据的列数
		int columnCount = metaData.getColumnCount();
		
		//获取元数据指定列的名字
		String columnLabel = metaData.getColumnLabel(2);
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import com.mchange.v2.c3p0.ComboPooledDataSource;

//数据库连接工具类
public class AiDButil {
	// 注意:本工具类在使用前需要确保基础准备工作已完成
	// 1 mysql c3p0共 3个jar包(log4j也可以添加)
	// 2 c3p0配置文件配置相应属性(如果导入了log4j那么也需要配置log4j配置文件)
	// 3 明确调用方法填入相应参数
	// (1)查询语句返回集合泛型类型属性必须与数据库列名一致(能多不能少)
	// (2)不确定参数填入的参数与sql中占位符?一致 且填入顺序一致

	// 需要导入mysql连接jar包与c3p0使用的两个jar包
	final static ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();

	// 获取连接方法
	// 返回一个连接对象
	public static Connection getCon() {
		// 连接使用c3p0进行获取
		// 使用c3p0数据库连接池获取连接
		Connection connection = null;
		try {
			connection = comboPooledDataSource.getConnection();
		} catch (SQLException e) {
			System.err.println("获取连接失败");
			return null;
		}
		return connection;
	}

	// DML方法
	// 不支持事务 单条sql语句执行
	public static boolean DML(String sql, Object... o) {
		// 获取连接
		Connection con = getCon();
		// 创建预编译对象
		try {
			PreparedStatement ps = con.prepareStatement(sql);
			for (int i = 0; i < o.length; i++) {
				ps.setObject((i + 1), o[i]);
			}
			ps.executeUpdate();
		} catch (SQLException e) {
			System.out.println("查询执行失败:" + sql);
			return false;
		}
		return true;
	}

	// DML方法
	// 支持事务 多条sql语句执行
	public static boolean DML(Connection con, String sql, Object... o) {
		// 创建预编译对象
		try {
			PreparedStatement ps = con.prepareStatement(sql);
			for (int i = 0; i < o.length; i++) {
				ps.setObject((i + 1), o[i]);
			}
			ps.executeUpdate();
		} catch (SQLException e) {
			System.out.println("查询执行失败:" + sql);
			return false;
		}
		return true;
	}

	// 查询dql语句方法
	public static <E> ArrayList<E> DQL(String sql, Class<E> c, Object... o) {
		ArrayList<E> list = new ArrayList<>();
		try {
			// 获取连接
			Connection con = getCon();
			// 准备预编译对象
			PreparedStatement ps = con.prepareStatement(sql);
			// 获取元数据 准备存储所有列名的数组
			ResultSetMetaData metaData = ps.getMetaData();
			// 创建指定长度用于存储列名的数组
			String[] names = new String[metaData.getColumnCount()];
			// 循环为names数组进行赋值
			for (int i = 0; i < names.length; i++) {
				names[i] = metaData.getColumnLabel(i + 1);// 获取指定列名
			}
			// 执行sql返回结果集
			ResultSet rs = ps.executeQuery();
			while (rs.next()) {

				// 每一行数据就是一个对象
				// 使用反射创建对象
				E obj = c.newInstance();

				// 当前行所有列名 在names数组中存储
				// 循环names数组取出当前行对应数据
				for (String colname : names) {
					Object value = rs.getObject(colname);// 获取列名对应值
					// 将值存入相应对象
					// 使用反射获取类中同名的属性对象
					Field field = c.getDeclaredField(colname);
					// 私有属性使用前必须赋权
					field.setAccessible(true);
					// 调用属性对象的set方法为指定对象进行赋值
					field.set(obj, value);
				}

				// 列名循环结束后对应对象属性已经全部进行赋值
				// 将对象存储至集合中
				list.add(obj);
			}

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return list;
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简单哟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值