`
lovnet
  • 浏览: 6693417 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

《partner4java 讲述JDBC》之第一步:JDBC基础

 
阅读更多
JDBC的作用:
The Java Database Connectivity (JDBC) 是一套Java API,用于Java编程语言和广泛的数据库之间连接的标准。JDBC API提供了一套访问“SQL数据库”的调用级API。


JDBC API概述:
JDBC API做的三件事情:
1、建立与数据库的连接(或访问系统数据源)
2、发送SQL语句
3、处理结果


JDBC架构:
JDBC API包括两部分接口:
第一部分是JDBC API提供给应用开发者(也就是我们这些码农);
第二部分是底层的JDBC驱动API,需JDBC驱动开发者实现。

如图:


左侧 -- 这种风格的驱动程序将JDBC调用 直接转换成 数据库管理系统使用的网络协议,通过Intranet访问直接从客户机调用DBMS服务器。
右侧 -- 这种风格的驱动程序将JDBC调用由一个中间件服务器,将其转换为DBMS协议,由中间件提供到不同的数据库的连接。


JDBC的优势:
使用现有的数据库 -- 使用JDBC技术,企业不会锁定任何专有的架构,可以很容易继续使用已安装的数据库。
简化企业开发 -- Java API和JDBC API结合,使得应用开发更容易和“廉价”。复杂的数据库访问任务被隐藏在JDBC之下,且JDBC API简单易学、易于部署。
网络计算机不需要配置 -- 使用JDBC API,不需要在客户端上做任何配置。所需的全部信息以JDBC URL或DataSource对象形式定义,通过Java编写的驱动程序进行链接。



本章内容分为下两大部分:
第一部分:HelloWorld简单示例
第二部分:JDBC详解





第一部分:HelloWorld简单示例



准备环境导入相关JDBC驱动(我这里用的是MySQL):
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>


一个基本的JDBC操作大体分为四步:
第一步:注册驱动
第二步:新建数据库连接
第三步:创建 用于执行静态SQL语句并返回它所生成结果 的对象
第四步:执行SQL 、获取返回结果并遍历

(希望你看完每一步讲解,自己动手完成一下)


第一步:注册驱动
向 DriverManager 注册给定驱动程序(可以注册多个不同的驱动程序)。

(1)为什么要注册驱动程序:
为了与特定的数据源相连,JDBC必须加装相应的驱动程序。
当获取连接时,DriverManager会试图从已注册的JDBC驱动程序集中选择一个适当的驱动程序。

(2)涉及到的两个类:
com.mysql.jdbc.Driver:
每个驱动程序类必须实现的接口。核心方法connect(String url, Properties info) 主要用于实现建立与数据库连接的底层协议。

javaw.sql.DriverManager:
管理一组 JDBC 驱动程序的基本服务。用来处理装载驱动程序并为创建新的数据库连接提供支持。

(3)如何操作
以MySQL为例:Class.forName("com.mysql.jdbc.Driver");
MySQL中你会遇到有些代码中注册的驱动不同:Class.forName("org.gjt.mm.mysql.Driver");
两者区别:
org.gjt.mm.mysql.Driver只是老版本遗留,为了兼容没有删除:
看下org.gjt.mm.mysql.Driver实现便知:
package org.gjt.mm.mysql;


import java.sql.SQLException;


public class Driver extends com.mysql.jdbc.Driver
{
}

(4)为什么这么注册
两点解释:
1、Class.forName -- 调用 forName("X") 将导致命名为 X 的类被初始化。这里的初始化为静态初始化。
2、看我们的com.mysql.jdbc.Driver
public class Driver extends NonRegisteringDriver
 implements java.sql.Driver
 {
 static
  {
     try
     {
       DriverManager.registerDriver(new Driver());
     } catch (SQLException E) {
       throw new RuntimeException("Can't register driver!");
     }
   }
 }


第二步:新建数据库连接
(1)涉及到的新类:
java.sql.Connection:
完成对某一指定数据库连接功能;与特定数据库的连接(会话);在连接上下文中执行 SQL 语句并返回结果。
Connection 对象的数据库能够提供描述其表、所支持的 SQL 语法、存储过程、此连接功能等等的信息。

(2)如何操作:分为三步
1、Connection conn = DriverManager.getConnection(url, user, password);
2、捕获意外 -- 在建立连接和对数据库进行操作的过程中,都可能产生意外。在JDBC中经常遇到的意外是SQLException。
3、关闭连接 -- conn.close(); 对于任何一个连接,当不再对数据源进行操作时,应该将其关闭。

(3)操作解释:
DriverManager类的getConnection方法用于建立与某个数据源的连接。
试图建立到给定数据库 URL 的连接。DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。
若连接成功,则返回一个Connection类的对象。以后对这个数据源的操作都是基于这个Connection对象。
getConnection方法是DriverManager类中的静态方法,所以使用时不用生成DriverManager类的对象,直接使用类名DriverManager调用就可以了。

参数:
url - jdbc:subprotocol:subname 形式的数据库 url
(如:jdbc:mysql://localhost:3306/jdbc_test
jdbc:mysql:对于MySQL来说是固定的协议格式;localhost为host;3306为数据库连接端口;jdbc_test为具体操作库名)
user - 数据库用户,连接是为该用户建立的
password - 用户的密码

Demo:
public static void main(String[] args) {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	
	try {
		// 加载 驱动程序
		Class.forName("com.mysql.jdbc.Driver");


		// 试图建立到给定数据库 URL 的连接。DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。
		conn = DriverManager.getConnection(url, user, password);
		System.out.println(conn);
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	}finally{
		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}


第三步:创建 用于执行静态SQL语句并返回它所生成结果 的对象
(1)相关对象
java.sql.Statement:
用于执行静态 SQL 语句并返回它所生成结果的对象。
Statement对象主要用于一般查询语句的执行,要想执行一个SQL查询语句,必须首先建立Statement对象。

(2)如何操作:和我们Connection操作类似
1、创建Statement对象 Connection.createStatement()方法用于建立一个Statement对象。
2、捕获异常
3、关闭Statement -- 每一个Statement对象在使用完毕后,都应该关闭。st.close();
立即释放此 Statement 对象的数据库和 JDBC 资源,而不是等待该对象自动关闭时发生此操作。一般来说,使用完后立即释放资源是一个好习惯,这样可以避免对数据库资源的占用。

Demo:
public static void main(String[] args) {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	Statement st = null;
	try {
		// 加载 驱动程序
		Class.forName("com.mysql.jdbc.Driver");


		// 试图建立到给定数据库 URL 的连接。DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。
		conn = DriverManager.getConnection(url, user, password);


		// 创建一个 Statement 对象来将 SQL 语句发送到数据库。
		st = conn.createStatement();


	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (st != null) {
				st.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}


第四步:执行SQL 、获取返回结果并遍历(万事俱备只欠东风)
(1)执行查询语句
设计的新类:
java.sql.ResultSet:
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet对象包括一个由查询语句返回的一个表,这个表中包含所有的查询结果。对ResultSet对象的处理必须逐行进行,而对每一行中的各个列,可以按任何顺序进行处理。

简单操作方式:
// 执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。
ResultSet rs = st.executeQuery("select * from user");

操作详解:
在Statement对象上,可以使用executeQuery方法来执行一个查询语句。executeQuery参数是一个String对象,即一个SQL的SELECT语句。
它的返回值是一个ResultSet类的对象。
在默认情况下,同一时间每个 Statement 对象在只能打开一个 ResultSet 对象,如果再次获获取会自动关闭上一个打开的ResultSet对象。

(2)遍历返回值 分为两步:
1、遍历
ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行;因为该方法在 ResultSet 对象没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
while (rs.next()) {

}
2、获取遍历光标当前行数据
ResultSet类的getXXX方法可以从某一列中获得结果。其中XXX是JDBC中的Java数据类型,如getString,getDate等。
getXXX方法接收一个指定要检索列的参数。
有两种指定列的方法:一种是以一个int值作为第几列的索引( 第一个列是 1,第二个列是 2,…… ),另一种是以一个String作为列名来索引(使用 SQL AS 子句指定的列标签。如果未指定 SQL AS 子句,则标签是列名称 )。

while (rs.next()) {
System.out.println(rs.getString("username"));
System.out.println(rs.getString("password"));
}

在对每一行进行处理时,可以对各个列按任意顺序进行处理。
不过,按从左至右的顺序对各列进行处理可以获得较高的执行效率。

数据转换:
ResultSet类的getXXX方法视图将结果集中的SQL数据类型转换为它所返回的Java数据类型。
如果使用getXXX方法去读取一个SQL NULL值,可能有以下几个情形:
1)返回为Java Object的getXXX方法将返回Java null值。
2)getByte,getShort,getInt,getLong,getFloat和getDouble将返回零值。
3)getBoolean将返回false值。

数据类型转换表:
java类型 SQL类型
boolean BIT
byte TINYINT
short SMALLINT
int INTEGER
long BIGINT
String CHAR,VARCHAR,LONGVARCHAR
byte array BINARY,VAR BINARY
java.sql.Date DATE
java.sql.Time TIME
java.sql.Timestamp TIMESTAMP

(3)关闭Statement相关
关闭 Statement 对象时,还将同时关闭其上获取的 ResultSet 对象(如果有)。

Demo:
public static void main(String[] args) {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	Statement st = null;
	try {
		// 加载 驱动程序
		Class.forName("com.mysql.jdbc.Driver");


		// 试图建立到给定数据库 URL 的连接。DriverManager 试图从已注册的 JDBC 驱动程序集中选择一个适当的驱动程序。
		conn = DriverManager.getConnection(url, user, password);


		// 创建一个 Statement 对象来将 SQL 语句发送到数据库。
		st = conn.createStatement();


		// 执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。
		ResultSet rs = st.executeQuery("select * from user");


		// 表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
		// ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行;因为该方法在
		// ResultSet 对象没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
		while (rs.next()) {


			// 在对每一行进行处理时,可以对各个列按任意顺序进行处理。
			// 不过,按从左至右的顺序对各列进行处理可以获得较高的执行效率。
			System.out.println(rs.getString("username"));
			System.out.println(rs.getString("password"));
		}
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (st != null) {
				st.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}



第二部分:JDBC详解


先提出几个问题:
1、执行executeQuery获取的ResultSet,ResultSet进行了关闭是否还可以遍历获取数据?
(几种情况会引起ResultSet关闭:1、自身主动关闭;2、Statement关闭)


2、数据库服务器关闭后ResultSet是否还可以遍历?(也就是说)


3、Connection关闭后ResultSet是否还可以遍历?


(自己动手写个Demo试一下,然后想一起能推断出来什么)


我们接下来的内容主要分为以下7块:
1、Statement两个重要子接口;
2、获取结果集的信息;
3、更新数据库操作;
4、输入参数;
5、事务;
6、批量执行。




1、Statement两个重要子接口

java.sql.Statement:
在一个给定的连接中作为SQL执行声明的容器,它包含了两个重要的子类型:
PreparedStatement:用于执行预编译的SQL声明;
CallableStatement:用于执行数据库中存储过程的调用;


PreparedStatement:
Statement对象在每次执行SQL语句时都将该语句传递给数据库,在多次执行同一语句时,这样效率很低。
这时可以使用PreparedStatement对象。如果数据库支持预编译,SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。

(1)创建PreparedStatement对象
从一个Connection对象可以创建一个PreparedStatement对象。在创建时,应该给出要预编译的SQL语句:
PreparedStatement ps = conn.prepareStatement("select * from user");

(2)执行查询语句
PreparedStatement对象也使用executeQuery方法来执行语句。与Statement类不同的是该方法没有参数。
这是由于创建PreparedStatement对象时,已经给出了要执行的SQL语句,并进行了预编译。
ResultSet rs = ps.executeQuery();
上诉语句可以被执行多次,无需重新给出SQL语句。

(3)关闭PreparedStatement
PreparedStatement对象也是使用close方法来关闭的,且也是必须关闭。

Demo:

public static void main(String[] args) throws FileNotFoundException, InterruptedException {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	PreparedStatement ps = null;
	try {
		PrintWriter writer = new PrintWriter(new File("error.txt"));
		
		DriverManager.setLogWriter(writer);
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(url, user, password);


		ps = conn.prepareStatement("select * from user");


		// 执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。
		ResultSet rs = ps.executeQuery();
		
		while (rs.next()) {
			System.out.println(rs.getString("username"));
			System.out.println(rs.getString("password"));
		}
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (ps != null) {
				ps.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

SQL 注入攻击:
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,从而利用系统的 SQL 引擎完成恶意行为的做法
对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement 取代 Statement 就可以了


CallableStatement:
CallableStatement对象用于执行数据库中的存储过程(stored procedure)。
CallableStatement类是PreparedStatement类派生的子类,因此它可使用PreparedStatement类和Statement类中的方法。

(1)创建CallableStatement对象
Connection类的prepareCall方法可以创建一个CallableStatement对象。
CallableStatement pc = conn.prepareCall("{call getUser()}");

(2)执行存储过程
ResultSet rs = pc.executeQuery();

(3)关闭CallableStatement
CallableStatement对象也是使用close方法来关闭的。

Demo:
public static void main(String[] args) throws FileNotFoundException, InterruptedException {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	CallableStatement pc = null;
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(url, user, password);


		pc = conn.prepareCall("{call getUser()}");


		ResultSet rs = pc.executeQuery();
		while (rs.next()) {
			System.out.println(rs.getString("username"));
			System.out.println(rs.getString("password"));
		}
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (pc != null) {
				pc.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}



2、获取结果集的信息


有时候我们无法知道结果集的表结构,可以使用ResultSet类的getMetaData方法来获取结果集的信息。
getColumnCount() -- 返回此 ResultSet 对象中的列数。
getColumnLabel(int column) -- 获取用于打印输出和显示的指定列的建议标题。(建议标题通常由 SQL AS 子句来指定。如果未指定 SQL AS,则从 getColumnLabel 返回的值将和 getColumnName 方法返回的值相同。 )
getColumnName(int column) -- 获取指定列的名称。
getColumnType(int column) -- 获取指定列的 SQL 类型。它的返回值是一个int。在java.sql.Types类中有关于各种SQL数据类型的定义。
getColumnTypeName(int column) -- 获取指定列的数据库特定的类型名称。
isReadOnly(int column) -- 指示指定的列是否明确不可写入。
isNullable(int column) -- 指示指定列中的值是否可以为 null。


Demo:
public static void main(String[] args) throws FileNotFoundException,
		InterruptedException {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	CallableStatement pc = null;
	try {
		Class.forName("com.mysql.jdbc.Driver");


		conn = DriverManager.getConnection(url, user, password);


		pc = conn.prepareCall("{call getUser()}");


		ResultSet rs = pc.executeQuery();


		ResultSetMetaData metaData = rs.getMetaData();
		for (int i = 0; i < metaData.getColumnCount(); i++) {
			System.out.println(metaData.getColumnName(i + 1));
		}


	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (pc != null) {
				pc.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

3、更新数据库操作

对一个表中的记录进行修改,插入和删除操作,分别对应SQL的UPDATE,INSERT和DELETE操作。
同SELECT操作类似,executeUpdate方法的参数是一个String对象,即要执行的SQL语句。它返回的不是ResultSet对象,而是一个整数。
对于UPDATE,INSERT和DELETE操作,这个整数是操作所影响的记录的行数。对于其他不返回值的SQL语句,executeUpdate方法的返回值是零。
st = conn.createStatement();
int count = st.executeUpdate("insert into user (username,password) values ('yes','233')");

st.executeUpdate("delete from user where id = 4");


使用PreparedStatement对象:
同SQL查询语句一样,数据更新语句也可以在PreparedStatement对象上执行。使用PreparedStatement对象,只需传递一次SQL语句,可以多次执行它。并且可以利用数据库的预编译技术,提高执行效率。

Demo:
public static void main(String[] args) throws FileNotFoundException,
		InterruptedException {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	Statement st = null;
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(url, user, password);


		st = conn.createStatement();


		int count = st
				.executeUpdate("insert into user (username,password) values ('yes','233')");
		System.out.println("影响行数:" + count);
		
		st.executeUpdate("delete from user where id = 4");
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (st != null) {
				st.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}



4、输入参数

(1)作用:
JDBC允许在要执行的SQL语句中设置参数,这样就给数据库操作带来很大的方便。
要想使用SQL语句的输入参数和输出参数,必须在PreparedStatement对象上进行操作。
由于CallableStatement类是PreparedStatement类的子类,所以在CallableStatement对象上的操作也可以使用输入参数和输出参数。

(2)使用规则:
PreparedStatement对象上查询语句和更新语句都可以设置输入参数。

在生成PreparedStatement对象时,在SQL语句中用"?"表明参数。

在执行SQL语句之前,使用setXXX方法给参数赋值,然后使用executeQuery或executeUpdate来执行这个SQL语句。
和我们getXXX获取数据方法类似:XXX是JDBC的数据类型,如:Int,String等;setXXX方法有两个参数,第一个是要赋值的参数(也就是"?")在SQL语句中的位置,SQL语句中的第一个参数的位置是1,第二个是2,以此类推;setXXX的第二个参数是要替换SQL相应位置占位符的值。

每一次执行SQL语句之前,可以给参数重新赋值。

Demo:

public static void main(String[] args) throws FileNotFoundException, InterruptedException {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	PreparedStatement ps = null;
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(url, user, password);


		ps = conn.prepareStatement("select * from user where username = ?");


		ps.setString(1, "hello");
		ResultSet rs = ps.executeQuery();
		while (rs.next()) {
			System.out.println(rs.getString("username"));
			System.out.println(rs.getString("password"));
		}
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (SQLException e) {
		e.printStackTrace();
	} finally {
		try {
			if (ps != null) {
				ps.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}


5、事务
事务本质:
哪怕只有一个步骤失败,则整个工作单元都必定失败。这就是大家所知的原子性(atomicity),则所有操作都作为一个原子单元来执行。


ACID,指数据库事务正确执行的四个基本要素的缩写.
包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
一个支持事务(Transaction)的数据库系统,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求.
  原子性
  整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  一致性
  当一个事务的开始和结束的时候,数据是处于一致的状态。例如,在一个应用程序,将资金从一个帐户到另一个,一致性属性确保在每个事务的开始和结束的两个帐户的资金的总价值是相同的。
  隔离性
  两个事务的执行是互不干扰的,一个事务不可能看到其他事务运行时,中间某一时刻的数据。
  持久性
  在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。


(ACID不是我们这里的重点,我就默认你已经学过了,不会的去查阅相关资料)


数据库环境:
mysql> show global variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+


mysql> show global variables like 'AUTOCOMMIT';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)


表:ENGINE=InnoDB


JDBC实现方式:
(事务很复杂,但是我们JDBC需要做的事情却非常简单)
1、调用 Connection 对象的 setAutoCommit(false) 以取消自动提交事务;
2、在所有的 SQL 语句都成功执行后,调用 commit() 方法提交事务;
3、在出现异常时,调用 rollback()方法回滚事务。



Demo:
public static void main(String[] args){
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	PreparedStatement ps = null;
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(url, user, password);
		conn.setAutoCommit(false);


		ps = conn.prepareStatement("update user set money = ? where id = ?");
		
		ps.setDouble(1, 8);
		ps.setInt(2, 1);
		ps.executeUpdate();
		
//			int i = 1/0;
		
		ps.setDouble(1, 12);
		ps.setInt(2, 2);
		ps.executeUpdate();
		
		conn.commit();
	} catch (Exception e) {
		if(conn != null){
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
		e.printStackTrace();
	} finally {
		try {
			if (ps != null) {
				ps.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}


6、批量执行
当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理具有更高效率。

JDBC的批量处理语句包括下面两个方法:
addBatch(String):添加需要批量处理的SQL语句或是参数;
executeBatch();执行批量处理语句;

通常我们会遇到两种批量执行SQL语句的情况:
多条SQL语句的批量处理;
一个SQL语句的批量传参;

Demo:
public static void main(String[] args) {
	String url = "jdbc:mysql://localhost:3306/jdbc_test";
	String user = "root";
	String password = "123456";
	Connection conn = null;
	PreparedStatement ps = null;
	try {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(url, user, password);


		ps = conn
				.prepareStatement("update user set money = ? where id = ?");


		ps.setDouble(1, 10);
		ps.setInt(2, 1);
		ps.addBatch();


		ps.setDouble(1, 10);
		ps.setInt(2, 2);
		ps.addBatch();


		ps.executeBatch();
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		try {
			if (ps != null) {
				ps.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics