Java基础之JDBC

一、PreparedStatement的使用

PreparedStatement是Statement的子接口,属于预处理操作。

获取PreparedStatement的方法:

使用Connection对象的PreparedStatement prepareStatement(sql)

使用PreparedStatement的好处

1、对于结构相同的SQL,可以提高执行效率

       在创建PreparedStatement对象时就指定了sql语句,该语句将被立即发送给DBMS进行编译。预编译的语句被存储在DBMS缓存中,下次执行相同的SQL语句时,则可以直接从缓存中取出来。

2、可以有效预防SQL注入

       在使用参数化查询的情况下,数据库系统(DBMS)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,即使参数中含有恶意的的指令,也不会被数据库所运行。

二、事务

概念:

数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作,要么全部成功,要么全部失败。

事务的四个特性(ACID)

1、原子性(Atomicity)

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

2、一致性(Consistency)

事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

3、隔离性(Isolation)

       事务的隔离性是指一个事物的执行不能被其他事务干扰,即一个事物内部的操作及使用的数据对并发的其他事务是隔离的,并发的执行各个事务之间不能相互干扰。

4、持久性(Durability)

       持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

事务的隔离级别

前提概念:

1、脏读(dirty read):一个事务读取到另一个事务未提交的数据(脏数据)的现象。

2、不可重复读:一个事务两次读取到的数据不一致的现象。

3、幻读:一个事务在读取数据时,另一个事务添加了数据记录,则第一个事务读取到了新添加的数据(与第一次读取数据比较)。

事务的隔离级别:

1、读未提交级别(read uncommitted)

读取到了另一个事务还未提交的数据。可能会发生脏读、不可重复读、幻读。

2、读提交级别(read committed)

只能读取其他事务已经提交的数据。可能会发生不可重复读、幻读。

3、可重复读级别(repeatable read,MySQL默认隔离级别)

在一个事务中可以重复读取相同的数据 。在InnoDB数据库存储引擎(此存储引擎支持事务)中,已经解决了幻读问题。

4、串行化级别(serializable)

最安全,但并发效率低。

隔离级别多线程并发读取数据时的正确性

connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

V:可能出现,X:不会出现


Java对事务的支持

       当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。

为了让多个SQL语句作为一个事务执行:

1、调用Connection对象的setAutoCommit(false);以取消自动提交事务

2、在所有的SQL语句都成功执行后,调用commit();方法提交事务

3、在出现异常时,调用rollback();方法回滚事务

4、若此时Connection没有被关闭,则需要恢复其自动提交状态


对数据库的一些小操作

1、查看当前会话(session)的隔离级别:

select @@tx_isolation;

2、设置当前会话的隔离级别

set session transaction isolation level read uncommitted | read committed | repeatable read

3、开启事务

start transaction;

4、提交事务

commit;

5、回滚整个事务

rollback;

6、设置保存点

savepoint 保存点名称;

7、回滚到保存点

rollback to 保存点名称;


PreparedStatement实例:

先创建账户表,并添加数据

create table account(

id int auto_increment primary key,

accname varchar(20) not null,

accpwd varchar(20) not null,

balance double

);

insert into account(accname,accpwd,balance)values('tom','123456',1000),('jerry','123456',200);

package util;

import java.io.*;
import java.sql.*;
import java.util.*;

//操作数据库的工具类
public class DBUtil {
	static {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	//获取数据库连接对象
	public static Connection getDBConnection(String url) {
		Properties pro = new Properties();
		InputStream input = null;
		Connection conn = null;
		try {
			input = DBUtil.class.getClassLoader().getResourceAsStream("db.Properties");
			pro.load(input);
			conn = DriverManager.getConnection(url,pro);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn;
	}
}
package preparedstatement;

import java.sql.*;

import util.DBUtil;

public class InsertDataDemo {

	public static void main(String[] args) {
		Connection conn = DBUtil.getDBConnection("jdbc:mysql://localhost:3306/mydb");
		PreparedStatement ps = null;
		String sql = "insert into student(stu_name,score)values(?,?)";
		try {
			//将SQL发送到DBMS中,进行预编译,将预编译好的SQL存储于DBMS缓冲区中
			ps = conn.prepareStatement(sql);
			for(int i = 1;i <= 72;i++) {
				ps.setString(1,"孙悟空"+i);
				ps.setDouble(2,89.5);
				ps.executeUpdate();//执行SQL
			}
			System.out.println("添加成功!");
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				ps.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

}
运行结果(数据库是部分截图):



事务实例:

package transaction;

import java.sql.*;

import util.DBUtil;

public class AccountDemo {
	public static void main(String[] args) {
		boolean isSuccess=transforMoney(1,2,200);
		if(isSuccess) {
			System.out.println("交易成功!");
		}else {
			System.out.println("sorry,ATM发生故障,转账失败!");
		}
	}
	
	public static boolean transforMoney(int fromId,int toId,double money) {
		boolean flag = false;
		Connection conn = DBUtil.getDBConnection("jdbc:mysql://localhost:3306/mydb");
		PreparedStatement ps = null;
		try {
			//取消自动提交
			conn.setAutoCommit(false);
			String sql1 = "update account set balance=balance-? where id=?";
			String sql2 = "update account set balance=balance+? where id=?";
			ps = conn.prepareStatement(sql1);
			ps.setDouble(1,money);
			ps.setInt(2,fromId);
			ps.executeUpdate();
			ps.close();
			
			ps = conn.prepareStatement(sql2);
			ps.setDouble(1,money);
			ps.setInt(2,toId);
			ps.executeUpdate();
			conn.commit();//提交当前事务
			flag = true;
		} catch (SQLException e) {
			try {
				conn.rollback();//回滚事务
				flag = false;
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} finally {
			try {
				ps.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return flag;
	}
}

运行结果:




注入(inject)实例:

package transaction;

import java.sql.*;

import util.DBUtil;

public class StopInject {

	public static void main(String[] args) {
		Connection conn = DBUtil.getDBConnection("jdbc:mysql://localhost:3306/mydb");
		PreparedStatement ps = null;
		ResultSet rs = null;
		String sql = "select * from account where accname=? and accpwd=?";
		try {
			ps = conn.prepareStatement(sql);
			String username = "tom";
			String password = "123456";
			ps.setString(1,username);
			ps.setString(2,password);
			rs = ps.executeQuery();
			if(rs.next()) {
				System.out.println("恭喜登陆成功~~~");
			}else {
				System.out.println("用户名或密码错误!请重新登陆。。。");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				rs.close();
				ps.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

}
运行结果:








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值