JAVA JDBC(存储过程和事务管理)
1.什么是存储过程
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。
2.存储过程的优点
(1)存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
(2)当对数据库进行复杂操作时(如对多个表进行Update,Insert,Query,Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。
(3)存储过程可以重复使用,可减少数据库开发人员的工作量
(4)安全性高,可设定只有某此用户才具有对指定存储过程的使用权
3.存储过程的缺点
(1)如果更改范围大到需要对输入存储过程的参数进行更改,或者要更改由其返回的数据,则您仍需要更新程序集中的代码以添加参数、更新 GetValue() 调用,等等,这时候估计比较繁琐了。
(2)可移植性差
由于存储过程将应用程序绑定到 SQL Server,因此使用存储过程封装业务逻辑将限制应用程序的可移植性。
4.存储过程数据库操作(Navicat 操作)
4.1调用入参的存储过程
(1)在navicat 的Student中 创建添加数据的函数(add_date)
public static void add(Student stu) throws Exception{
//通过工具类,获取数据库链接对象
Connection conn= DBUtil.getConn();
//创建 sql 语句
String sql = "insert Student (name,age,address) value (?,?,?)";
//创建 预加载 的sql 语句执行对象
PreparedStatement ptmt=conn.prepareStatement(sql);
//给名字赋值
ptmt.setString(1, stu.getName());
//给年龄赋值
ptmt.setInt(2, stu.getAge());
ptmt.setString(3, stu.getAddress());
//执行 sql 语句
ptmt.execute();
}
4.2调用出参存储过程
(1)创建记录数据条数的函数(get_record_count)
4.3调用无参存储过程
(1)创建一个打印所有数据的函数(select_all)
(2)代码实例
public class StudentProcedureDao {
/**
* 1、查询数据
*/
public ResultSet select_all() {
ResultSet rs = null;
// 1 获取数据库连接对象
Connection conn = DBUtil.getConn();
try {
// 2 创建存储过程调用对象
CallableStatement cs = conn.prepareCall("call select_all()");
// 3 执行存储过程
cs.execute();
// 4 获取结果集
rs = cs.getResultSet();
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
5.jdbc 存储过程实例代码如下:
//DBUtil 连接数据库
package com.j1702.db;
import java.sql.*;
public class DBUtil {
private static final String URL="jdbc:mysql://127.0.0.1:3306/jdbc_test";
private static final String USER="root";
private static final String PASSWORD=""; private static Connection conn = null;
static { try {
// 1.加载mysql驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获得数据库链接对象conn
setConn(DriverManager.getConnection(URL, USER, PASSWORD));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
public static Connection getConn() {
return conn;
}
public static void setConn(Connection conn) {
DBUtil.conn = conn;
} } //model 层
package com.j1702.model; public class Student {
private Integer id;
private String name;
private Integer age;
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
} }
//dao 层
package com.j1702.dao;
import java.sql.*;
import java.util.*; import com.j1702.db.DBUtil;
import com.j1702.model.Student; public class StudentDao { //1.增
public static void add(Student stu) throws Exception{
//通过工具类,获取数据库链接对象
Connection conn= DBUtil.getConn();
//创建 sql 语句
String sql = "insert Student (name,age,address) value (?,?,?)";
//创建 预加载 的sql 语句执行对象
PreparedStatement ptmt=conn.prepareStatement(sql);
//给名字赋值
ptmt.setString(1, stu.getName());
//给年龄赋值
ptmt.setInt(2, stu.getAge());
ptmt.setString(3, stu.getAddress()); //执行 sql 语句
ptmt.execute(); }
//2.删
public static void delete(Integer id) throws Exception{
//通过工具类,获取数据库链接对象
Connection conn= DBUtil.getConn();
//创建 sql 语句
String sql = " delete from Student where id =?";
//创建 预加载 的sql 语句执行对象
PreparedStatement ptmt=conn.prepareStatement(sql);
//给名字赋值
ptmt.setInt(1,id); ptmt.execute(); }
//3.改
public static void update(Student stu) throws Exception{
//通过工具类,获取数据库链接对象
Connection conn= DBUtil.getConn();
//创建 sql 语句
String sql = "update Student set name=?,age=?,address=? where id =?";
//创建 预加载 的sql 语句执行对象
PreparedStatement ptmt=conn.prepareStatement(sql);
//给名字赋值
ptmt.setString(1, stu.getName());
//给年龄赋值
ptmt.setInt(2, stu.getAge());
ptmt.setString(3, stu.getAddress());
ptmt.setInt(4, stu.getId()); //执行 sql 语句
ptmt.execute(); }
//4.查所有
public static void query(Student stu ) throws Exception{
//通过工具类,获取数据库链接对象
Connection conn= DBUtil.getConn();
//创建 sql 语句
String sql = "select * from Student";
//创建 预加载 的sql 语句执行对象
Statement stmt=conn.createStatement();
ResultSet rs=stmt.executeQuery(sql); while(rs.next()){
System.out.println("id:"+rs.getInt("id")+"\t \tname:"+rs.getString("name")+"\tage"+
rs.getInt("age")+"\taddress"+rs.getString("address"));
} }
//5.查某个
public static void queryWithId(Integer id) throws Exception{ Connection conn= DBUtil.getConn();
//创建 sql 语句
String sql = "select * from Student where id=?";
//创建 预加载 的sql 语句执行对象
PreparedStatement ptmt= conn.prepareStatement(sql);
ptmt.setInt(1, id);
ResultSet rs=ptmt.executeQuery(); while(rs.next()){
System.out.println("id:"+rs.getInt("id")+"\t \tname:"
+rs.getString("name")+"\tage:"+rs.getInt("age")+"\taddress"+rs.getString("address"));
} }
//6.条件查询
// public static void queryWithParam(List<Map<String,Object>>param) throws Exception{
//
// Connection conn= DBUtil.getConn();
// //创建 sql 语句
// StringBuffer sql = new StringBuffer();
// sql.append("select * from Student where 1=1");
// for (Map<String, Object> map : param) {
// sql.append(" and "+map.get("key")
// +"="+map.get("value"));
//创建 预加载 的sql 语句执行对象
// PreparedStatement ptmt= conn.prepareStatement(sql.toString());
//
// ResultSet rs=ptmt.executeQuery();
//
// while(rs.next()){
// System.out.println("id:"+rs.getInt("id")+"\t \tname:"
// +rs.getString("name")+"\tage:"+rs.getInt("age")+"\taddress:"+rs.getString("address"));
// }
// }
public static void queryWithParam1(List<Map<String,Object>>param) throws Exception{ Connection conn= DBUtil.getConn();
//创建 sql 语句
StringBuffer sql = new StringBuffer();
sql.append("select * from Student where 1=1"); for (Map<String, Object> map : param) {
sql.append(" and "+map.get("key")
+" like '%"+map.get("value")+"%'");
//创建 预加载 的sql 语句执行对象
System.out.println(sql);
PreparedStatement ptmt= conn.prepareStatement(sql.toString()); ResultSet rs=ptmt.executeQuery(); while(rs.next()){
System.out.println("id:"+rs.getInt("id")+"\t \tname:"
+rs.getString("name")+"\tage:"+rs.getInt("age")+"\taddress:"+rs.getString("address"));
System.out.println(sql);
}
}
}
public static void queryWithParam2(List<Map<String,Object>>param) throws Exception{ Connection conn= DBUtil.getConn();
//创建 sql 语句
StringBuffer sql = new StringBuffer();
sql.append("select * from Student where 1=1");
for (Map<String, Object> map : param) {
sql.append(" and "+map.get("key")
+"="+map.get("value")); //创建 预加载 的sql 语句执行对象
PreparedStatement ptmt= conn.prepareStatement(sql.toString()); ResultSet rs=ptmt.executeQuery(); while(rs.next()){
System.out.println("id:"+rs.getInt("id")+"\t \tname:"
+rs.getString("name")+"\tage:"+rs.getInt("age")+"\taddress:"+rs.getString("address"));
}
}
} public static void queryWithParam3(List<Map<String,Object>>param) throws Exception{ Connection conn= DBUtil.getConn();
//创建 sql 语句
StringBuffer sql = new StringBuffer();
sql.append("select * from Student where 1=1");
for (Map<String, Object> map : param) {
sql.append(" and "+map.get("key")
+"="+map.get("value")); //创建 预加载 的sql 语句执行对象
PreparedStatement ptmt= conn.prepareStatement(sql.toString()); ResultSet rs=ptmt.executeQuery(); while(rs.next()){
System.out.println("id:"+rs.getInt("id")+"\t \tname:"
+rs.getString("name")+"\tage:"+rs.getInt("age")+"\taddress:"+rs.getString("address"));
}
}
} } //action 行动层
package com.j1702.action; import com.j1702.dao.StudentDao;
import com.j1702.model.Student;
//这一层是我们的controller 层:他是view和model层的服务员
public class StudentAction {
//想数据库中插入一条数据
public static void insert(Student stu) throws Exception{
StudentDao.add( stu);
} //根据传入的id 删除学生
public static void delete(Integer id) throws Exception{
StudentDao.delete(id);
}
//更新学生信息数据
public static void update(Student stu) throws Exception{
StudentDao.update(stu);
}
//查找Student
public static void find(Integer id) throws Exception{
StudentDao.queryWithId(id);
}
} //view 视图界面层
package com.j1702.view; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner; import com.j1702.dao.*;
import com.j1702.db.DBUtil;
import com.j1702.model.Student; public class TestView {
public static void main(String[] args) throws Exception{
//编程以终端为view 层,实现数据的增、删、改
/**
* 请输入你要做的操作:A-添加,D-删除,U更新,F-查询,exit-退出
*
* A 请输入插入的数据 name,age,address
*
*/
login(); } public static void login(){
System.out.println("请输入你要做的操作:A-添加,D-删除,U更新,F-查询,exit-退出");
Scanner in= new Scanner (System.in);
String xz=in.next();
switch(xz){
case "A" :
try {
add();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case "D":
try {
delete();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case "U":
try {
update();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case "F" :
find();
break;
case "exit":
exit(); break;
default :
System.out.println("没有你输入的选项,请重新选择");
login(); } }
public static void add() throws Exception{
Student stu=new Student(); System.out.println("请输入插入的数据 name:");
Scanner cn=new Scanner (System.in);
String name=cn.next();
System.out.println("请输入插入的数据 age:");
Scanner ca=new Scanner (System.in);
int age=ca.nextInt();
System.out.println("请输入插入的数据 address :");
Scanner cd=new Scanner (System.in);
String address = cd.next(); stu.setName(name);
stu.setAge(age);
stu.setAddress(address);
StudentDao l1 =new StudentDao();
l1.add(stu);
System.out.println("数据插入成功!"); System.out.println("请选择:1 继续插入数据, 2.返回上一层");
Scanner in= new Scanner (System.in);
int x2=in.nextInt();
switch (x2) {
case 1:
add();
break;
case 2:
login();
break;
default:
System.out.println("没有你要的选项,请重新输入!"); } }
public static void delete() throws Exception{
Connection conn= DBUtil.getConn();
System.out.println("请输入你要删除数据的ID");
Scanner in=new Scanner (System.in);
Integer xid=in.nextInt();
StudentDao l2=new StudentDao();
l2.delete(xid);
System.out.println("数据删除成功!"); System.out.println("请选择:1 继续继续数据, 2.返回上一层");
Scanner in1= new Scanner (System.in);
int x2=in1.nextInt();
switch (x2) {
case 1:
delete();
break;
case 2:
login();
break;
default:
System.out.println("没有你要的选项,请重新输入!"); }
}
public static void update() throws Exception{ Student stu1=new Student();
System.out.println("请输入修改的数据 ID:");
Scanner ci=new Scanner (System.in);
int id=ci.nextInt(); System.out.println("请输入修改的数据 name:");
Scanner cn1=new Scanner (System.in);
String name=cn1.next();
System.out.println("请输入修改的数据 age:");
Scanner ca1=new Scanner (System.in);
int age=ca1.nextInt();
System.out.println("请输入修改的数据 address :");
Scanner cd1=new Scanner (System.in);
String address = cd1.next();
stu1.setId(id);
stu1.setName(name);
stu1.setAge(age);
stu1.setAddress(address);
StudentDao l3=new StudentDao();
l3.update(stu1); System.out.println("数据修改成功!");
System.out.println("请选择:1 继续修改数据, 2.返回上一层");
Scanner in1= new Scanner (System.in);
int x2=in1.nextInt();
switch (x2) {
case 1:
update();
break;
case 2:
login();
break;
default:
System.out.println("没有你要的选项,请重新输入!"); }
}
public static void find(){
System.out.println("请选择:1.查询所有 2.查询某个 3.条件查询");
Scanner in= new Scanner (System.in); int xz3=in.nextInt();
switch(xz3){
case 1:
Student stu=new Student();
StudentDao st=new StudentDao();
try {
st.query(stu);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cgf();
break;
case 2:
System.out.println("请输入要查询的ID:");
Scanner cid=new Scanner(System.in);
Integer id1=cid.nextInt(); try {
StudentDao st1=new StudentDao();
st1.queryWithId(id1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} cgf(); break;
case 3:
tiaojian();
break;
} }
public static void exit(){
System.out.println("谢谢使用,再见!");
return; }
public static void cgf(){
System.out.println("数据查询成功!"); System.out.println("请选择:1 继续查询数据, 2.返回上一层");
Scanner in3= new Scanner (System.in);
int x3=in3.nextInt();
switch (x3) {
case 1:
find();
break;
case 2:
login();
break;
default:
System.out.println("没有你要的选项,请重新输入!"); }
}
public static void cgf1(){
System.out.println("数据查询成功!"); System.out.println("请选择:1 继续查询数据, 2.返回上一层");
Scanner in3= new Scanner (System.in);
int x3=in3.nextInt();
switch (x3) {
case 1:
tiaojian();
break;
case 2:
login();
break;
default:
System.out.println("没有你要的选项,请重新输入!"); }
} public static void tiaojian(){
System.out.println("请选择要查询的字段:1.id 查询 2.name 查询 3.age 查询 4.address 查询");
Scanner tjcx=new Scanner(System.in);
int tx3=tjcx.nextInt();
switch(tx3){
case 1:
System.out.println("请输入要查询的ID:");
Scanner cid=new Scanner(System.in);
Integer id1=cid.nextInt(); try {
StudentDao st1=new StudentDao();
st1.queryWithId(id1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} cgf1();
break;
case 2:
System.out.println("请输入你要查询的 name:");
Scanner cn1=new Scanner(System.in); Map<String,Object> map = new HashMap<String,Object>();
map.put("key", "name");
map.put("value",cn1.next());
List<Map<String,Object>> li= new ArrayList<Map<String,Object>>();
li.add(map);
try {
StudentDao l4=new StudentDao();
l4.queryWithParam1(li);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cgf1();
break;
case 3:
System.out.println("请输入你要查询的 age:");
Scanner cn2=new Scanner(System.in);
String sage=cn2.next();
Map<String,Object> map1 = new HashMap<String,Object>();
map1.put("key", "age");
map1.put("value",sage);
List<Map<String,Object>> li1= new ArrayList<Map<String,Object>>();
li1.add(map1);
try {
StudentDao l4=new StudentDao();
l4.queryWithParam2(li1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cgf1();
break;
case 4:
System.out.println("请输入你要查询的 address:");
Scanner cn3=new Scanner(System.in);
String sadd=cn3.next();
Map<String,Object> map2 = new HashMap<String,Object>();
map2.put("key", "address");
map2.put("value",sadd);
List<Map<String,Object>> li2= new ArrayList<Map<String,Object>>();
li2.add(map2);
try {
StudentDao l4=new StudentDao();
l4.queryWithParam3(li2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
cgf1();
break; } }
}
6.java JDBC 事务管理
6.1事务的基本概念:事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
6.2数据库开启事务命令
方式一:利用SQL语句管理事务
start transaction;--开启事务,这条语句之后的sql语句将处在一个事务当中,这些sql语句并不会立即执行
Commit--提交事务,一旦提交事务,事务中的所有sql语句才会执行。
Rollback -- 回滚事务,将之前所有的sql取消。
方式二:在数据库中存在一个自动提交变量,通过show variables like '%commit%'-----autocommit 值是on,说明开启了事务自动提交。
可以 set autocommint = off(set autocommint=0),关闭自动提交,此时输入的sql语句就不会自动提交了,需要手动roolback或commit
6.3使用事务
(1)当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
(2)JDBC控制事务语句
Connection.setAutoCommit(false); // 相当于start transaction
Connection.rollback(); rollback
Connection.commit(); commit
(3)事务管理实例代码:
(1)当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
(2)JDBC控制事务语句
Connection.setAutoCommit(false); // 相当于start transaction
Connection.rollback(); rollback
Connection.commit(); commit
7.事务的特性(ACID)
(1)原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
(2)一致性(Consistency)
事务前后数据的完整性必须保持一致。
(3)隔离性(Isolation)
事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
(4)持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
8.事务隔离级别(Transaction Isolation Levels)
JDBC定义了五种事务隔离级别:
TRANSACTION_NONE JDBC 驱动不支持事务
TRANSACTION_READ_UNCOMMITTED 允许脏读、不可重复读和幻读。
TRANSACTION_READ_COMMITTED 禁止脏读,但允许不可重复读和幻读。
TRANSACTION_REPEATABLE_READ 禁止脏读和不可重复读,单运行幻读。
TRANSACTION_SERIALIZABLE 禁止脏读、不可重复读和幻读。
9.事务隔离性的设置语句
数据库共定义了四种隔离级别:
Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
Read committed:可避免脏读情况发生(读已提交)
Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
set [global/session] transaction isolation level 设置事务隔离级别
select @@tx_isolation查询当前事务隔离级别
安全性来说:Serializable>Repeatable read>Read committed>Read uncommitted
效率来说:Serializable<Repeatable read<Read committed<Read uncommitted
通常来说,一般的应用都会选择Repeatable read或Read committed作为数据库隔离级别来使用。
mysql默认的数据库隔离级别为:REPEATABLE-READ
如何查询当前数据库的隔离级别?select @@tx_isolation;
如何设置当前数据库的隔离级别?set [global/session] transaction isolation level ...;
~此种方式设置的隔离级别只对当前连接起作用。
set transaction isolation level read uncommitted;
set session transaction isolation level read uncommitted;
~此种方式设置的隔离级别是设置数据库默认的隔离级别
set global transaction isolation level read uncommitted;
10.JDBC事务的优缺点
JDBC为使用Java进行数据库的事务操作提供了最基本的支持。通过JDBC事务,我们可以将多个SQL语句放到同一个事务中,保证其ACID特性。JDBC事务的主要优点就是API比较简单,可以实现最基本的事务操作,性能也相对较好。
但是,JDBC事务有一个局限:一个 JDBC 事务不能跨越多个数据库!!!
所以,如果涉及到多数据库的操作或者分布式场景,JDBC事务就无能为力了。
11.java JDBC 事务实例代码如下:
//db 层
package com.j1702.db;
import java.sql.*; public class DBUtil {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/jdbc_trading";
private static final String USER = "root";
private static final String PASSWORD = "";
private static Connection conn = null;
// (static{}(即static块),会在类被加载的时候执行且仅会被执行一次,一般用来初始化静态变量和调用静态方法,)
static {
try {
// 1 加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 2 获得数据的连接
conn = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (ClassNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
// 静态方法获取连接
public static Connection geConnection() {
return conn;
} } //model 层 package com.j1702.model; // 转账记录表的映射
public class Transfer { private Integer id; // 主键
private String from_where; // 转账人
private String to_where; // 收款人
private Integer money; // 转账金额
private String time; // 转账时间 // 无参构造
public Transfer(){} // 有参构造
public Transfer( String from_where, String to_where, Integer money) {
this.from_where = from_where;
this.to_where = to_where;
this.money = money;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getFrom_where() {
return from_where;
} public void setFrom_where(String from_where) {
this.from_where = from_where;
} public String getTo_where() {
return to_where;
} public void setTo_where(String to_where) {
this.to_where = to_where;
} public Integer getMoney() {
return money;
} public void setMoney(Integer money) {
this.money = money;
} public String getTime() {
return time;
} public void setTime(String time) {
this.time = time;
} } package com.j1702.model; public class User { private Integer id; // 主键
private String name; // 用户名
private Integer money; // 用户余额 public User(){} public User(String name, Integer money){
this.name = name;
this.money = money;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Integer getMoney() {
return money;
} public void setMoney(Integer money) {
this.money = money;
} } // dao 层
package com.j1702.dao; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List; import com.j1702.db.DBUtil;
import com.j1702.model.Transfer; public class TransferDao { // 增加
public void addRecord(Transfer t) throws SQLException {
Connection conn = DBUtil.geConnection();
String sql = "insert Transfer (from_where,to_where,money) values(?,?,?)";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setString(1, t.getFrom_where());
ptmt.setString(2, t.getTo_where());
ptmt.setInt(3, t.getMoney());
ptmt.execute();
} // 查询
public List<Transfer> queryTransfers() throws Exception {
Connection conn = DBUtil.geConnection();
Statement stmt = conn.createStatement();
ResultSet res = stmt.executeQuery("select * from Transfer");
List<Transfer> t_list = new ArrayList<Transfer>();
Transfer t = null;
while (res.next()) {
t = new Transfer();
t.setFrom_where(res.getString("from_where"));
t.setTo_where(res.getString("to_where"));
t.setMoney(res.getInt("money"));
t.setTime(res.getString("time"));
t_list.add(t);
}
return t_list;
}
} package com.j1702.dao; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import com.j1702.db.DBUtil;
import com.j1702.model.User; public class UserDao {
// 修改(因为转账会发生 money 的增加或减少)
public void updateUser(User u) throws SQLException {
Connection conn = DBUtil.geConnection();
String sql = "update User set money=? where name=?";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setInt(1, u.getMoney());
ptmt.setString(2, u.getName());
ptmt.execute();
} // 查询
public User queryUser(String name) throws Exception {
Connection conn = DBUtil.geConnection();
String sql = "select * from User where name=?";
PreparedStatement ptmt = conn.prepareStatement(sql);
ptmt.setString(1, name);
ResultSet res = ptmt.executeQuery();
User u = new User();
while (res.next()) {
u.setName(res.getString("name"));
u.setMoney(res.getInt("money"));
}
return u;
}
} package com.j1702.action; import java.util.List; import com.j1702.dao.TransferDao;
import com.j1702.model.Transfer; public class TransferAction { // 1 新增转账记录
public void add(Transfer t) throws Exception{
TransferDao dao = new TransferDao();
dao.addRecord(t);
} // 2 查询转账记录
public List<Transfer> query() throws Exception{
TransferDao dao = new TransferDao();
return dao.queryTransfers();
}
} package com.j1702.action; import java.sql.SQLException; import com.j1702.dao.UserDao;
import com.j1702.model.User; public class UserAction { public User query(String name) throws Exception{
UserDao dao = new UserDao();
return dao.queryUser(name);
} public void update(User u) throws SQLException{
UserDao dao = new UserDao();
dao.updateUser(u);
}
} //view 层
package com.j1702.view; import java.sql.Connection;
import java.util.List;
import java.util.Scanner; import com.j1702.action.TransferAction;
import com.j1702.action.UserAction;
import com.j1702.db.DBUtil;
import com.j1702.model.Transfer;
import com.j1702.model.User; public class TestView { public static void main(String[] args) throws Exception { Scanner scan = new Scanner(System.in);
while(true){
System.out.println("请操作指令:1-转账;2-查询;其他任意键退出");
String operator = scan.nextLine(); switch (operator) {
case "1":
{
System.out.println("请输入付款人姓名:");
String from_name = scan.nextLine();
System.out.println("请输入收款人姓名:");
String to_name = scan.nextLine();
System.out.println("请输入转账金额:");
Integer money = scan.nextInt();
scan.nextLine();
Transfer tf = new Transfer(from_name, to_name, money); // 处理数据库操作
UserAction ua = new UserAction();
// 查询
User u1 = ua.query(from_name);
User u2 = ua.query(to_name);
// 操作金额的增减
u1.setMoney(u1.getMoney()-money);
u2.setMoney(u2.getMoney()+money); // 更新 需要添加事务 Connection conn = DBUtil.geConnection();
//设置自动提交为false
conn.setAutoCommit(false); //需要绑定执行的代码
ua.update(u1);
ua.update(u2); // 增加转账记录
TransferAction ta = new TransferAction();
ta.add(tf); //手动提交事务
conn.commit();
}
break;
case "2":
{
TransferAction ta = new TransferAction();
List<Transfer> l_t = ta.query();
for (Transfer transfer : l_t) {
System.out.println(transfer.getTime()
+ "\t->\t"
+ transfer.getFrom_where()
+ "\t->\t"
+ transfer.getTo_where()
+ "\t->\t"
+ transfer.getMoney()
);
}
}
break;
default:
{
System.out.println("退出!");
scan.close();
System.exit(0);
}
break;
}
}
}
}
12.JTA事务
为什么需要JTA
通常,JDBC事务就可以解决数据的一致性等问题,鉴于他用法相对简单,所以很多人关于Java中的事务只知道有JDBC事务,或者有人知道框架中的事务(比如Hibernate、Spring)等。但是,由于JDBC无法实现分布式事务,而如今的分布式场景越来越多,所以,JTA事务就应运而生。
如果,你在工作中没有遇到JDBC事务无法解决的场景,那么只能说你做的项目还都太小。拿电商网站来说,我们一般把一个电商网站横向拆分成商品模块、订单模块、购物车模块、消息模块、支付模块等。然后我们把不同的模块部署到不同的机器上,各个模块之间通过远程服务调用(RPC)等方式进行通信。以一个分布式的系统对外提供服务。
一个支付流程就要和多个模块进行交互,每个模块都部署在不同的机器中,并且每个模块操作的数据库都不一致,这时候就无法使用JDBC来管理事务。我们看一段代码:
/** 支付订单处理 **/
@Transactional(rollbackFor = Exception.class)
public void completeOrder() {
orderDao.update(); // 订单服务本地更新订单状态
accountService.update(); // 调用资金账户服务给资金帐户加款
pointService.update(); // 调用积分服务给积分帐户增加积分
accountingService.insert(); // 调用会计服务向会计系统写入会计原始凭证
merchantNotifyService.notify(); // 调用商户通知服务向商户发送支付结果通知
}
上面的代码是一个简单的支付流程的操作,其中调用了五个服务,这五个服务都通过RPC的方式调用,请问使用JDBC如何保证事务一致性?我在方法中增加了@Transactional
注解,但是由于采用调用了分布式服务,该事务并不能达到ACID的效果。
JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接。下列任一个Java平台的组件都可以参与到一个JTA事务中:JDBC
连接、JDO PersistenceManager
对象、JMS
队列、JMS
主题、企业JavaBeans(EJB
)、一个用J2EE Connector Architecture
规范编译的资源分配器。
JTA的定义
Java事务API(Java Transaction API
,简称JTA ) 是一个Java企业版 的应用程序接口,在Java环境中,允许完成跨越多个XA资源的分布式事务。
JTA和它的同胞Java事务服务(JTS;Java TransactionService),为J2EE平台提供了分布式事务服务。不过JTA只是提供了一个接口,并没有提供具体的实现,而是由j2ee服务器提供商 根据JTS规范提供的,常见的JTA实现有以下几种:
- 1.J2EE容器所提供的JTA实现(JBoss)
- 2.独立的JTA实现:如JOTM,Atomikos.这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用。
JTA里面提供了 java.transaction.UserTransaction
,里面定义了下面几个方法
begin
:开启一个事务
commit
:提交当前事务
rollback
:回滚当前事务
setRollbackOnly
:把当前事务标记为回滚
setTransactionTimeout
:设置事务的事件,超过这个事件,就抛出异常,回滚事务
这里,值得注意的是,不是使用了UserTransaction
就能把普通的JDBC操作直接转成JTA操作,JTA对DataSource、Connection和Resource 都是有要求的,只有符合XA规范,并且实现了XA规范的相关接口的类才能参与到JTA事务中来,关于XA规范,请看我的另外一篇文章中有相关介绍。这里,提一句,目前主流的数据库都支持XA规范。
要想使用用 JTA 事务,那么就需要有一个实现
javax.sql.XADataSource
、javax.sql.XAConnection
和javax.sql.XAResource
接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个XADataSource
对象就是一个XAConnection
对象的工厂。XAConnection
是参与 JTA 事务的 JDBC 连接。要使用JTA事务,必须使用
XADataSource
来产生数据库连接,产生的连接为一个XA连接。XA连接(
javax.sql.XAConnection
)和非XA(java.sql.Connection
)连接的区别在于:XA可以参与JTA的事务,而且不支持自动提交。
public void JtaTransfer() {
javax.transaction.UserTransaction tx = null;
java.sql.Connection conn = null;
try{
tx = (javax.transaction.UserTransaction) context.lookup("java:comp/UserTransaction"); //取得JTA事务,本例中是由Jboss容器管理
javax.sql.DataSource ds = (javax.sql.DataSource) context.lookup("java:/XAOracleDS"); //取得数据库连接池,必须有支持XA的数据库、驱动程序
tx.begin();
conn = ds.getConnection();
// 将自动提交设置为 false,
//若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交
conn.setAutoCommit(false);
stmt = conn.createStatement();
// 将 A 账户中的金额减少 500
stmt.execute("\
update t_account set amount = amount - 500 where account_id = 'A'");
// 将 B 账户中的金额增加 500
stmt.execute("\
update t_account set amount = amount + 500 where account_id = 'B'");
// 提交事务
tx.commit();
// 事务提交:转账的两步操作同时成功
} catch(SQLException sqle){
try{
// 发生异常,回滚在本事务中的操做
tx.rollback();
// 事务回滚:转账的两步操作完全撤销
stmt.close();
conn.close();
}catch(Exception ignore){
}
sqle.printStackTrace();
}
}
上面的例子就是一个使用JTA事务的转账操作,该操作相对依赖于J2EE容器,并且需要通过JNDI的方式获取UserTransaction
和Connection
。
标准的分布式事务
一个分布式事务(Distributed Transaction)包括一个事务管理器(
transaction manager
)和一个或多个资源管理器(resource manager
)。一个资源管理器(resource manager
)是任意类型的持久化数据存储。事务管理器(transaction manager
)承担着所有事务参与单元者的相互通讯的责任。
JTA的实现方式也是基于以上这些分布式事务参与者实现的,具体的关于JTA的实现细节不是本文的重点,感兴趣的同学可以阅读JTA 深度历险 – 原理与实现
- 看上面关于分布式事务的介绍是不是和2PC中的事务管理比较像?的却,2PC其实就是符合XA规范的事务管理器协调多个资源管理器的一种实现方式。 我之前有几篇文章关于2PC和3PC的,那几篇文章中介绍过分布式事务中的事务管理器是如何协调多个事务的统一提交或回滚的,后面我还会有几篇文章详细的介绍一下和分布式事务相关的内容,包括但不限于全局事务、DTP模型、柔性事务等。
JTA的优缺点
JTA的优点很明显,就是提供了分布式事务的解决方案,严格的ACID。但是,标准的JTA方式的事务管理在日常开发中并不常用,因为他有很多缺点:
- 实现复杂
- 通常情况下,JTA UserTransaction需要从JNDI获取。这意味着,如果我们使用JTA,就需要同时使用JTA和JNDI。
- JTA本身就是个笨重的API
- 通常JTA只能在应用服务器环境下使用,因此使用JTA会限制代码的复用性。
总结
Java事务的类型有三种:JDBC事务
、JTA(Java Transaction API)事务
、容器事务
,其中JDBC的事务操作用法比较简单,适合于处理同一个数据源的操作。JTA事务相对复杂,可以用于处理跨多个数据库的事务,是分布式事务的一种解决方案。
这里还要简单说一下,虽然JTA事务是Java提供的可用于分布式事务的一套API,但是不同的J2EE平台的实现都不一样,并且都不是很方便使用,所以,一般在项目中不太使用这种较为负责的API。现在业内比较常用的分布式事务解决方案主要有异步消息确保型、TCC、最大努力通知等。关于这几种分布式事务解决方案,我会在后面的文章中介绍。欢迎关注与交流。
JAVA JDBC(存储过程和事务管理)的更多相关文章
- Java数据库连接--JDBC调用存储过程,事务管理和高级应用
相关链接:Jdbc调用存储过程 一.JDBC常用的API深入详解及存储过程的调用 1.存储过程的介绍 我们常用的操作数据库语言SQL语句在执行的时候要先进行编译,然后执行,而存储过程是在大型数据库系统 ...
- 创建JDBC模板简化代码、JDBC应用的事务管理以及连接池的作用
一.创建JDBC模板简化代码 一个简单的查询.要做这么一大堆事情,并且还要处理异常,我们不防来梳理一下: 1.获取connection 2.获取statement 3.获取resultset 4 ...
- MySQL存储过程之事务管理
原文链接:http://hideto.iteye.com/blog/195275 MySQL存储过程之事务管理 ACID:Atomic.Consistent.Isolated.Durable 存储程序 ...
- 【Spring实战】—— 16 基于JDBC持久化的事务管理
前面讲解了基于JDBC驱动的Spring的持久化管理,本篇开始则着重介绍下与事务相关的操作. 通过本文你可以了解到: 1 Spring 事务管理的机制 2 基于JDBC持久化的事务管理 Spring的 ...
- Java EE开发课外事务管理平台
Java EE开发课外事务管理平台 演示地址:https://ganquanzhong.top/edu 说明文档 一.系统需求 目前课外兴趣培训学校众多,完善,但是针对课外兴趣培训学校教务和人事管理信 ...
- java框架之Spring(3)-JDBC模板使用&事务管理
下面内容使用到的 jar 包下载 JDBC模板使用 入门 1.导包,如要导入 Spring 的基本开发包.数据库驱动包.Spring 提供的 JDBC 模板包,如下: 2.测试: @Test publ ...
- Spring---AOP注解开发&jdbc模板&Spring事务管理
一.AOP注解开发 此处需要回忆一遍AOP的概念.简单的来说,AOP就是利用动态代理技术,做到不触动源代码但却扩展了功能.那么就需要一个被扩展的对象和一个“新的功能”,例如说给某类的saveUser方 ...
- 四、spring的JDBC模板和事务管理
Spring的JDBC模板 Spring是JavaEE开发的一站式框架,对各种持久化技术都提供了简单的模板 ORM持久化技术 模板类 JDBC org.springframework.jdbc.cor ...
- (二十四)JDBC应用的事务管理(转账事例)
目录 利用 Dbutils 进行事务操作(以转账为例) 转账实现方式(不优雅的方式) ThreadLocal 类 转账实现方式(优雅的方式) 利用 Dbutils 进行事务操作(以转账为例) 我们只在 ...
随机推荐
- Java package和import语句
Java中的package和import语句 如果你想让其他人访问你的类,你一定要把你写的类放到正确的子目录下. 在Java里,对于位于包中的类是这样管理的: Java编译器把包对应于文件系统的目录管 ...
- block知识总结
一.block在内存中存在的形式 1.当把block句法写在函数或者方法外面时,系统会在静态数据区分配一块内存区域给block对象.这片区域在程序执行期会一直存在. 2.当block句法写在函数或者方 ...
- Java容器之Iterator接口
Iterator 接口: 1. 所有实现了Collection接口的容器类都有一个iterator方法用以返回一个实现了Iterator接口的对象. 2. Iterator 对象称作迭代器,用以方便的 ...
- Thinkphp5获取数据库数据到视图
这是学习thinkhp5的基础篇笔记. 本文主要讲怎么配置数据库链接,以及查询数据库数据,并且最后将数据赋给视图. 数据库配置: thinkphp5的数据库配置默认在conf下的database.ph ...
- SQL SERVER技术内幕之3 联接查询
JOIN表运算符对两个输入表进行操作.联接有三种基本类型:交叉联接.内联接和外联接.这三种联接的区别是它们采用的逻辑查询处理步骤各不相同,每种联接都有一套不同的步骤.交叉联接只有一个步骤----笛卡尔 ...
- c#中语句的先后顺序对结果的影响
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test ...
- perf使用的问题,再看perf record,perf record 设置的采样频率,采样频率是如何体现在
当perf stat -e branches 是统计 再看perf record,perf record是为了是记录时间发生的时候的调用栈, 在我的测试代码中总共有200,000,000条branch ...
- JAVA IDE IntelliJ IDEA使用简介(二)—之基本操作
一.在编辑器中打开文件 1.可以使用下面的几种方式打开project内的文件进行编辑 (·)在project窗口中双击需要编辑的文件. (·)在project窗口选择需要编辑的文件,按F4 ( ...
- SQL查询数据总结
SQL查询数据 完整语法 Select [select选项] 字段列表[字段别名]/* from 数据源 [where条件子句] [group by子句] [having子句] [order by子句 ...
- Qt快速入门学习笔记(基础篇)
本文基于Qter开源社区论坛版主yafeilinux编写的<Qt快速入门系列教程目录>,网址:http://bbs.qter.org/forum.php?mod=viewthread&am ...