MySQL语法语句大全
一、SQL速成
结构查询语言(SQL)是用于查询关系数据库的
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
语言,它包括若干关键字和一致的语法,便于数据库元件(如表、索引、字段等)的建立和操纵。
以下是一些重要的SQL快速参考,有关SQL的语法和在标准SQL上增加的特性,请查询MySQL
手册
华为质量管理手册 下载焊接手册下载团建手册下载团建手册下载ld手册下载
。
1.创建表
表是数据库的最基本元素之一,表与表之间可以相互独立,也可以相互关联。创建表的基本语法如下:
create table table_name
(column_name data无效 {identity |null|not null},
…)
其中参数table_name和column_name必须满足用户数据库中的识别器(identifier)的要求,参数data无效是一个标准的SQL类型或由用户数据库提供的类型。用户要使用non-null从句为各字段输入数据。
create table还有一些其他选项,如创建临时表和使用select子句从其他的表中读取某些字段组成新表等。还有,在创建表是可用PRIMARY KEY、KEY、INDEX等标识符设定某些字段为主键或索引等。
书
关于书的成语关于读书的排比句社区图书漂流公约怎么写关于读书的小报汉书pdf
写上要注意:
在一对圆括号里的列出完整的字段清单。
字段名间用逗号隔开。
字段名间的逗号后要加一个空格。
最后一个字段名后不用逗号。
所有的SQL陈述都以分号";"结束。
例:
mysql> CREATE TABLE test (blob_col BLOB, index(blob_col(10)));
2.创建索引
索引用于对数据库的查询。一般数据库建有多种索引方案,每种方案都精于某一特定的查询类。索引可以加速对数据库的查询过程。创建索引的基本语法如下:
create index index_name
on table_name (col_name[(length)],... )
例:
mysql> CREATE INDEX part_of_name ON customer (name(10));
3.改变表结构
在数据库的使用过程中,有时需要改变它的表结构,包括改变字段名,甚至改变不同数据库字段间的关系。可以实现上述改变的命令是alter,其基本语法如下:
alter table table_name alter_spec [, alter_spec ...]
例:
mysql> ALTER TABLE t1 CHANGE a b INTEGER;
4.删除数据对象
很多数据库是动态使用的,有时可能需要删除某个表或索引。大多数数据库对象可以下面的命令删除:
drop object_name
mysql> DROP TABLE tb1;
5.执行查询
查询是使用最多的SQL命令。查询数据库需要凭借结构、索引和字段类型等因素。大多数数据库含有一个优化器(optimizer),把用户的查询语句转换成可选的形式,以提高查询效率。
值得注意的是MySQL不支持SQL92标准的嵌套的where子句,即它只支持一个where子句。其基本语法如下:
SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [HIGH_PRIORITY]
[DISTINCT | DISTINCTROW | ALL]
select_expression,...
[INTO {OUTFILE | DUMPFILE} ’file_name’ export_options]
[FROM table_references
][WHERE where_definition]
[GROUP BY col_name,...]
[HAVING where_definition]
[ORDER BY {unsigned_integer | col_name | formula} ][ASC | DESC] ,...]
[LIMIT ][offset,] rows]
[PROCEDURE procedure_name] ]
其中where从句是定义选择标准的地方,where_definition可以有不同的格式,但都遵循下面的形式:
字段名操作表达式
字段名操作字段名
在第一种形式下,标准把字段的值与表达式进行比较;在第二种形式下,把两个字段的值进行比较。根据所比较的数据类型,search_condition中的操作可能选以下几种:
= 检查是否相等
!= 检查是否不等
> (或>=) 检查左边值是否大于(或大于等于)右边值
< (或<=) 检查左边值是否小于(或小于等于)右边值
[not] between 检查左边值是否在某个范围内
[not] in 检查左边是否某个特定集的成员
[not] like 检查左边是否为右边的子串
is [not] null 检查左边是否为空值
在这里,可以用通配符_代表任何一个字符,%代表任何字符串。使用关键字
、和可以生成复杂的词,它们运行检查时使用布尔表达式的多重标准集。
例:
mysql> select t1.name, t2.salary from employee AS t1, info AS t2 where t1.name = t2.name;
mysql> select college, region, seed from tournament
ORDER BY region, seed;
mysql> select col_name from tbl_name WHERE col_name > 0;
6.修改表中数据
在使用数据库过程中,往往要修改其表中的数据,比如往表中添加新数据,删除表中原有数据,或对表中原有数据进行更改。它们的基本语法如下:
数据添加:
insert [into] table_name [(column(s))]
values (expression(s))
例:
mysql> INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2);
数据删除:
删除 from table_name where search_condition
数据更改:
更新 table_name
set column1=expression1,
column2=expression2,…
where search_condition
7.数据库切换
当存在多个数据库时,可以用下面的命令定义用户想使用的数据库:
use database_name
8.统计函数
SQL有一些统计函数,它们对于生成数据表格很有帮助。下面介绍几个常用的统计函数:
sum (exepression) 计算表达式的和
avg (exepression) 计算表达式的平均值
count (exepression) 对表达式进行简单的计数
count (*) 统计记录数
max (exepression) 求最大值
min (exepression) 求最小值
其中exepression为任何有效的SQL表达式,它可以是一个或多个记录,也可以是别的SQL函数的组合。
二、MySQL使用导引
1.运用MySQL建立新数据库
在shell下运行:
$>mysqladmin create database01
Database "database01" created.
2.启动MySQL
在shell下运行:
$>mysql
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 22 to server version: 3.21. 29a-gamma-debug
无效 ’help’ for help.
3.更换数据库
mysql>use database01
database changed.
4.创建表
mysql>create table table01 (field01 integer, field02 char(10));
Query OK, 0 rows affected (0.00 sec)
5.列出表清单
mysql>show tables;
Tables in database01
Table01
table02
6.列出表中的字段清单
mysql>show columns from table01;
Field 无效 Null Key Default Extra
field01 int(11) YES
field02 char(10) YES
7.表的数据填写
插入数据
mysql>insert into table01 (field01, field02) values (1, ’first’);
Query OK, 1 row affected (0.00 sec)
8.字段的增加
...一次一个字段
mysql>alter table table01 add column field03 char(20);
Query OK, l row affected (0.04 sec)
Records: 1 Duplicates: 0 Warnings: 0
...一次多个字段
mysql>alter table table01 add column field04 date, add column field05 time;
Query OK, l row affected (0.04 sec)
Records: 1 Duplicates: 0 Warnings: 0
注意:每一列都必须以"add column"重新开始。
它运行了吗?让我们看看。
mysql>select * from table01;
field01 field02 field03 field04 field05
1 first NULL NULL NULL
9.多行命令输入
MySQL命令行界面允许把陈述作为一行输入,也可以把它展开为多行输入。这两者之间并没有语法上的区别。使用多行输入,你可以将SQL陈述一步步分解,从而使你更容易理解。
在多行方式下,注释器把每一行都添加到前面的行后,直到你用分号";"来结束这个SQL陈述。一旦键入分号并按回车键,这个陈述即被执行。
下面的例子是同一个严格的SQL陈述的两种输入方法:
单行输入
Mysql>create table table33 (field01 integer, field02 char(30));
多行输入
Mysql>create table table33
->(field01
->integer,
->field02
->char(30));
注意不能将单词断开,如:
正确
mysql>create table table33
->( field01
->integer,
->field02
->char(30));
错误
mysql>create table table33
->( field01 inte
->ger,
->field02
->char(30));
当插入或更改数据时,不能将字段的字符串展开到多行里,否则硬回车将被储存到数据中:
标准操作
mysql>insert into table33 (field02)
->values
->(’who thought of foo?’);
硬回车储存到数据中
mysql>insert into table33 (field02)
->values
->(’who thought
->of foo?’);
结果如下:
mysql>select * from table33;
field01 field02
NULL who thought of foo?
NULL who thought
Of foo?
10.表的数据嵌入
mysql>insert into table01 (field01, field02, field03, field04, field05) values
->(2, ’second’, ’another’, ’1999-10-23’, ’10:30:00’);
Query OK, 1 row affected (0.00 sec)
标准日期格式是"yyyy-mm-dd"。
标准时间格式是"hh:mm:ss"。
引号内要求所给的是上述的标准日期和时间格式。
日期也可以"yyyymmdd"形式,时间也可以"hhmmss"形式输入,但其值不需要再加引号。
数字值不需要加引号。这种保存与数据类型无关,这些数据类型都有格式化的专栏来包含(例如:文本,日期,时间,整数等)。
MySQL有一个很有用的命令缓冲区。它保存着你目前已经键入的SQL语句利用它,对于相同的命令,你就不必一遍又一遍地重复输入。下一步我们就来看这样的一个例子。
利用命令缓冲区(及任意的日期和时间格式)增加另一个数据
按两次键盘上的向上箭头键。
回车。
在圆括号内输入新的值,并以分号结尾。
(3, ’a third’, ’more’, 19991024, 103004);
回车。
新值存在里面了吗?
mysql>select * from table01;
field01 field02 field03 field04 field05
1 first NULL NULL NULL
2 second another 1999-10-23 10:30:00
3 a third more 1999-10-24 10:30:04
11.表的数据更新
一次修改一个字段
再次注意语法。文本需要加引号但数字不要。
mysql>更新 table01 set field03=’new info’ where field01=1;
Query OK, 1 row affected (0.00 sec)
一次改变多个字段
记住在每一个更新的字段间用逗号隔开。
mysql>更新 table01 set field04=19991022, field05=062218 where field01=1;
Query OK, 1 row affected (0.00 sec)
一次更新多个数据
mysql>更新 table01 set field05=152901 where field04>19990101;
Query OK, 3 rows affected (0.00 sec)
12.删除数据
mysql>删除 from table01 where field01=3;
Query OK, 1 row affected (0.00 sec)
13.退出
mysql>quit
Bye
------------------------------(书是借的,记录下来,方便查看------------------------------------------------------------------------------------
1.使用show 语句查看服务器上存在的数据库
mysql>show databases;
2.创建一个数据库 example
mysql>CREATE DATABASE example;
3.选择数据库
mysql>use example;
4使用show语句查看数据库中存在的表.
mysql>show tables;
5.创建一个数据库。建立一个班级同学的生日表,表的内容包括同学姓名、性别、出生日期。
mysql>CTEATE TABLE mytable
->(name VARCHAR(20),
->SEX CHAR(1),
->birth DATE;
6显示表的结构
mysql>DESCRIBE mytable;
7往表中加入记录
先用select 语句来查看表中的数据
mysql>select * from mytable;
Empty set (0.00 sec)
加入一条新记录
mysql>insert into mytable
->values('Echo','f','1983-05-14');
再用上面的select语句可以发现mytable表中多了一项新记录.
8用文本方式将数据装入一个数据库表。如果一条一条输入会很麻烦。可以用文本文件的方式将所有记录加入数据库中。
在g:\code下创建一个文本文件"mysql.txt",每行包含一个记录,用定位符(tab)把值分开,并且以在create table 语句中列出的列次序给出,如:
Jerry m 1977-07-07
mary f 1978-12-12
commy f 1970-09-02
可以使用命令装文件文件"mysql.txt"装载到mytable表中
mysql>LOAD DATA LOCAL INFILE "g:\\code\\mysql.txt' INTO TABLE mytable;
查看:
mysql>select * from mytable;
删除表:
mysql>drop table mytable;
删除数据库
mysql>drop database example;
用show databases语句查看结果;
SELECT --从数据库表中检索数据行和列
INSERT --向数据库表添加新数据行
DELETE --从数据库表中删除数据行
UPDATE --更新数据库表中的数据
--数据定义
CREATE TABLE --创建一个数据库表
DROP TABLE --从数据库中删除表
ALTER TABLE --修改数据库表结构
CREATE VIEW --创建一个视图
DROP VIEW --从数据库中删除视图
CREATE INDEX --为数据库表创建一个索引
DROP INDEX --从数据库中删除索引
CREATE PROCEDURE --创建一个存储过程
DROP PROCEDURE --从数据库中删除存储过程
CREATE TRIGGER --创建一个触发器
DROP TRIGGER --从数据库中删除触发器
CREATE SCHEMA --向数据库添加一个新模式
DROP SCHEMA --从数据库中删除一个模式
CREATE DOMAIN --创建一个数据值域
ALTER DOMAIN --改变域定义
DROP DOMAIN --从数据库中删除一个域
--数据控制
GRANT --授予用户访问权限
DENY --拒绝用户访问
REVOKE --解除用户访问权限
--事务控制
COMMIT --结束当前事务
ROLLBACK --中止当前事务
SET TRANSACTION --定义当前事务数据访问特征
--程序化SQL
DECLARE --为查询设定游标
EXPLAN --为查询描述数据访问
计划
项目进度计划表范例计划下载计划下载计划下载课程教学计划下载
OPEN --检索查询结果打开一个游标
FETCH --检索一行查询结果
CLOSE --关闭游标
PREPARE --为动态执行准备SQL 语句
EXECUTE --动态地执行SQL 语句
DESCRIBE --描述准备好的查询
---局部变量
declare @id char(10)
--set @id = ''''10010001''''
select @id = ''''10010001''''
---全局变量
---必须以@@开头
--IF ELSE
declare @x int @y int @z int
select @x = 1 @y = 2 @z=3
if @x > @y
print ''''x > y'''' --打印字符串''''x > y''''
else if @y > @z
print ''''y > z''''
else print ''''z > y''''
--CASE
use pangu
update employee
set e_wage =
case
when job_level = ’1’ then e_wage*1.08
when job_level = ’2’ then e_wage*1.07
when job_level = ’3’ then e_wage*1.06
else e_wage*1.05
end
--WHILE CONTINUE BREAK
declare @x int @y int @c int
select @x = 1 @y=1
while @x < 3
begin
print @x --打印变量x 的值
while @y < 3
begin
select @c = 100*@x + @y
print @c --打印变量c 的值
select @y = @y + 1
end
select @x = @x + 1
select @y = 1
end
--WAITFOR
--例 等待1 小时2 分零3 秒 ......
1.增加列
ALTER TABLE table_name ADD( column datatype [DEFAULT EXPR][,column datatype...]);
例如:
SQL>ALTER TABLE emp01 ADD eno NUMBER(4);
2.修改列定义
例如:
SQL>ALTER TABLE emp01 MODIFY job VARCHAR2(15)
2 DEFAULT 'CLERK'
3.删除列
例如:
SQL> ALTER TABLE emp01 DROP COLUMN dno;
4.修改列名
例如:
SQL>ALTER TABLE emp01 RENAME COLUMN eno TO empno;
5.修改表名
例如:
SQL>RENAME emp01 TO employee;
[SQL]:如何简化JDBC代码
问题的提出
在一个应用程序中,处理JDBC的操作是一个重复率较高的工作。当你在一个JDBC数据源上执行SQL查询时,你通常需要执行下面几个步骤:
1.生成SQL语句
2.获得连接
3.获得一个PreparedStatement对象
4.在PreparedStatement对象中设定需要送入到数据库的值
5.执行SQL语句
6.处理查询结果
除此之外,你还需要处理SQLException异常。如果上面列出的这些步骤分散在程序的各个部分的话,程序中需要多个try/catch块来处理异常。
如果我们仔细看看上面列出的步骤,就会发现在执行不同的SQL语句时,上面这些步骤中涉及到的程序代码变化不会很大:我们使用同样的方法获得数据库连接和PreperedStatement对象;使用setXXX方法来设定PreperedStatement对象中的值;处理SQL查询结果的过程也是基本不变的。在这篇文章中,我们通过定义三个JDBC模型,去除了上述六个步骤中的三个步骤,这样使得整个过程更加简单,而且具有更好的通用性。
查询模型
我们定义了一个名叫SQLProcessor的类,在该类中定义了一个executeQuery()方法来执行SQL语句,我们在实现这个方法的时候尽量保持代码的简洁性,并且传递尽可能少的参数给该方法。下面是该方法的定义:
public Object[] executeQuery(String sql, Object[] pStmntValues,
ResultProcessor processor);
我们知道在执行SQL语句的JDBC过程中,变化的因素有三个:SQL语句,PreparedStatement对象和如何解释和处理查询结果。在上面的方法定义中,sql中保存的就是SQL语句;pStmntValues对象数组保存的是需要放入preparedStatement对象中的值;processor参数是一个能够处理查询结果的对象,于是我们把JDBC程序涉及到的对象分成了三个部分。下面让我们来看一下executeQuery()和与它相关的一些方法的实现:
public class SQLProcessor {
public Object[] executeQuery(String sql, Object[] pStmntValues,
ResultProcessor processor) {
//获得连接
Connection conn = ConnectionManager.getConnection();
//将SQL语句的执行重定向到handlQuery()方法
Object[] results = handleQuery(sql, pStmntValues, processor, conn);
//关闭连接
closeConn(conn);
//返回结果
return results;
}
protected Object[] handleQuery(String sql, Object[] pStmntValues,
ResultProcessor processor, Connection conn)
{
//获得一个preparedStatement对象
PreparedStatement stmnt = null;
try {
//获得preparedStatement
stmnt = conn.prepareStatement(sql);
//向preparedStatement中送入值
if(pStmntValues != null) {
PreparedStatementFactory.buildStatement(stmnt, pStmntValues);
}
//执行SQL语句
ResultSet rs = stmnt.executeQuery();
//获得查询结果
Object[] results = processor.process(rs);
//关闭preparedStatement对象
closeStmnt(stmnt);
//返回结果
return results;
//处理异常
} catch(SQLException e) {
String message = "无法执行查询语句 " + sql;
//关闭所有资源
closeConn(conn);
closeStmnt(stmnt);
//抛出DatabaseQueryException
throw new DatabaseQueryException(message);
}
}
}
...
}
程序中有两个方法需要说明:PreparedStatementFactory.buildStatement()和processor.process()。buildStatement()方法把在pStmntValues对象数组中的所有对象送到prepareStatement对象中的相应位置。例如:
...
//取出对象数组中的每个对象的值,
//在preparedStatement对象中的相应位置设定对应的值
for(int i = 0; i < values.length; i++) {
//如果对象的值为空, 设定SQL空值
if(value instanceof NullSQLType) {
stmnt.setNull(i + 1, ((NullSQLType) value).getFieldType());
} else {
stmnt.setObject(i + 1, value);
}
}
因为stmnt.setOject(int index, Object value)方法不能接受一个空对象作为参数。为了使程序能够处理空值,我们使用了自己
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
的NullSQLType类。当一个NullSQLType对象被初始化的时候,将会保存数据库表中相应列的SQL类型。在上面的例子中我们可以看到,NULLSQLType对象的属性中保存了一个SQL NULL实际对应的SQL类型。我们用NULLSQLType对象的getFieldType()方法来向preparedStatement对象填入空值。
下面让我们来看一看processor.process()方法。Processor类实现了ResultProcessor接口,该接口是用来处理SQL查询结果的,它只有一个方法process(),该方法返回了处理SQL查询结果后生成的对象数组。
public interface ResultProcessor {
public Object[] process(ResultSet rs) throws SQLException;
}
process()的典型实现方法是遍历查询后返回的ResultSet对象,将保存在ResultSet对象中的值转化为相应的对象放入对象数组。下面我们通过一个例子来说明如何使用这些类和接口。例如当我们需要从数据库的一张用户信息表中取出用户信息,表名称为User:
列名
数据类型
ID
NUMBER
UserName
VARCHAR2
Email
VARCHAR2
我们需要在程序中定义一个类User来映射上面的表:
public User(int id, String userName, String email)
如果我们使用常规方法来读取User表中的数据,我们需要一个方法来从数据库表中读取数据,然后将数据送入User对象中。而且一旦查询语句发生变化,我们需要修改大量的代码。让我们看一看采用本文描述的解决方案情况会如何。
首先构造一个SQL语句。
private static final String SQL_GET_USER = "SELECT * FROM USERS WHERE ID = ?";
然后创建一个ResultProcessor接口的实例类,通过它我们可以从查询结果中获得一个User对象。
public class UserResultProcessor implements ResultProcessor {
// 列名称定义(省略)
...
public Object[] process(ResultSet rs) throws SQLException {
// 使用List对象来保存所有返回的User对象
List users = new ArrayList();
User user = null;
// 如果查询结果有效,处理查询结果
while(rs.next()) {
user = new User(rs.getInt(COLUMN_ID), rs.getString(COLUMN_USERNAME),
rs.getString(COLUMN_EMAIL));
users.add(user);
}
return users.toArray(new User[users.size()]);
最后,将执行SQL查询和返回User对象的指令放入getUser()方法中。
public User getUser(int userId) {
// 生成一个SQLProcessor对象并执行查询
SQLProcessor processor = new SQLProcessor();
Object[] users = processor.executeQuery(SQL_GET_USER_BY_ID,
new Object[] {new Integer(userId)},
new UserResultProcessor());
// 返回查询到的第一个User对象
return (User) users[0];
}
这就是我们需要做的全部工作:只需要实现一个processor类和一个getUser()方法。与传统的JDBC程序相比,在本文描述的模型中,我们不需要处理数据库连接操作,生成prepareStatement对象和异常处理部分的代码。如果需要在同一张表中根据用户名查用户ID,我们只需要在代码中申明新的查询语句,然后重用UserResultProcessor类中的大部分代码。
更新模型
如果SQL语句中涉及到更新,情况又会怎样呢?我们可以用类似于设计查询模型的方法来设计更新模型,我们需要向SQLProcessor类中增加一些新的方法。这些方法同executeQuery()和handleQuery()方法有相似之处,只是我们需要改变一下处理ResultSet对象的代码,并且把更新的行数作为方法的返回值。
public void executeUpdate(String sql, Object[] pStmntValues,
UpdateProcessor processor) {
// 获得数据库连接
Connection conn = ConnectionManager.getConnection();
// 执行SQL语句
handleUpdate(sql, pStmntValues, processor, conn);
// 关闭连接
closeConn(conn);
}
protected void handleUpdate(String sql, Object[] pStmntValues,
UpdateProcessor processor, Connection conn) {
PreparedStatement stmnt = null;
try {
stmnt = conn.prepareStatement(sql);
// 向prepareStatement对象中送入值
if(pStmntValues != null) {
PreparedStatementFactory.buildStatement(stmnt, pStmntValues);
}
// 执行更新语句
int rows = stmnt.executeUpdate();
// 统计有多少行数据被更新
processor.process(rows);
closeStmnt(stmnt);
// 异常处理
} catch(SQLException e) {
String message = "无法执行查询语句 " + sql;
closeConn(conn);
closeStmnt(stmnt);
throw new DatabaseUpdateException(message);
}
}
上面的两个方法和处理查询的方法不同之处在于他们如何处理返回值。由于更新语句只需要返回被更新了的行数,所以我们不需要处理SQL操作返回的结果。实际上有些情况下连被更新了的行数都不需要返回,我们这样做的原因是在某些情况下需要确认更新操作已经完成。
我们设计了UpdateProcessor接口来处理Update操作返回的更新行数。
public interface UpdateProcessor {
public void process(int rows);
}
例如在程序中需要保证更新操作,更新表中的至少一条记录。在UpdateProcessor接口的实现类中就可以加入对修改行数的检测,当没有记录被更新时,processor()方法可以抛出自定义的异常;也可以将更新的行数记录到日志文件中;或者激发一个自定义的更新事件。总而言之,你可以在其中做任何事。
下面是一个使用更新模型的例子:
首先生成SQL语句
private static final String SQL_UPDATE_USER =
"UPDATE USERS SET USERNAME = ?, EMAIL = ? WHERE ID = ?";
实现UpdateProcessor接口。在Processor()方法中,检查Update操作是否更新了数据。如果没有,抛出IllegalStateException异常。
public class MandatoryUpdateProcessor implements UpdateProcessor {
public void process(int rows) {
if(rows < 1) {
String message = "更新操作没有更新数据库表中的数据。";
throw new IllegalStateException(message);
}
}
}
最后在updateUser()方法中执行Update操作并处理结果。
public static void updateUser(User user) {
SQLProcessor sqlProcessor = new SQLProcessor();
sqlProcessor.executeUpdate(SQL_UPDATE_USER,
new Object[] {user.getUserName(),
user.getEmail(),
new Integer(user.getId())},
new MandatoryUpdateProcessor());
}
事务模型
在数据库中,事务和独立的SQL语句的区别在于事务在生命期内使用一个数据库连接,并且AutoCommit属性必须被设为False。因此我们需要指定事务何时开始,何时结束,并且在事务结束时提交事务。我们可以重用SQLProcessor中的大部分代码来处理事务。也许在最开始读者会问为什么要把执行更新和处理更新的工作放在 executeUpdate()和handleUpdate()两个函数中完成--实际上它们是可以被合并到同一个函数中的。这样做的原因是把处理数据库连接的代码和处理SQL操作的代码分离开来。对于需要在多个SQL操作间共用数据库连接的事务模型来说,这种方案便于编码。
在事务中,我们需要保存事务的状态,特别是数据库连接的状态。前面的SQLProcessor中没有保存状态的属性,为了保证对SQLProcessor类的重用,我们设计了一个包装类,该类包装了SQLProcessor类,并且可以维护事务在生命周期内的状态。
public class SQLTransaction {
private SQLProcessor sqlProcessor;
private Connection conn;
// 缺省构造方法,该方法初始化数据库连接,并将AutoCommit设定为False
...
public void executeUpdate(String sql, Object[] pStmntValues,
UpdateProcessor processor) {
// 获得结果。如果更新操作失败,回滚到事务起点并抛出异常
try {
sqlProcessor.handleUpdate(sql, pStmntValues, processor, conn);
} catch(DatabaseUpdateException e) {
rollbackTransaction();
throw e;
}
}
public void commitTransaction() {
// 事务结束,提交更新并回收资源
try {
conn.commit();
sqlProcessor.closeConn(conn);
// 如果发生异常,回滚到事务起点并回收资源
} catch(Exception e) {
rollbackTransaction();
throw new DatabaseUpdateException("无法提交当前事务");
}
}
private void rollbackTransaction() {
// 回滚到事务起点并回收资源
try {
conn.rollback();
conn.setAutoCommit(true);
sqlProcessor.closeConn(conn);
// 如果在回滚过程中发生异常,忽略该异常
} catch(SQLException e) {
sqlProcessor.closeConn(conn);
}
}
}
SQLTransaction中出现了一些新方法,这些方法主要是用来处理数据库连接和进行事务管理的。当一个事务开始时,SQLTransaction对象获得一个新的数据库连接,并将连接的AutoCommit设定为False,随后的所有SQL语句都是用同一个连接。
只有当commitTransaction()被调用时,事务才会被提交。如果执行SQL语句的过程中发生了异常,程序会自动发出一个回滚申请,以恢复程序对数据库所作的改变。对于开发人员来说,不需要担心在出现异常后处理回滚或关闭连接的工作。下面是一个使用事务模型的例子。
public static void updateUsers(User[] users) {
// 开始事务
SQLTransaction trans = sqlProcessor.startTransaction();
// 更新数据
User user = null;
for(int i = 0; i < users.length; i++) {
user = users[i];
trans.executeUpdate(SQL_UPDATE_USER,
new Object[] {user.getUserName(),
user.getFirstName(),
user.getLastName(),
user.getEmail(),
new Integer(user.getId())},
new MandatoryUpdateProcessor());
}
// 提交事务
trans.commitTransaction();
}
在例子中我们只使用了更新语句(在大多数情况下事务都是由更新操作构成的),查询语句的实现方法和更新语句类似。
问题
在实际使用上面提到的这些模型时,我遇到了一些问题,下面是这些问题的小结,希望对大家有所帮助。
自定义数据库连接
在事务处理的时候,有可能发生在多个事务并存的情况下,它们使用的数据库连接不同的情况。ConnectionManager需要知道它应该从数据库连接池中取出哪一个连接。你可以简单修改一下模型来满足上面的要求。例如在executeQuery()和executeUpdate()方法中,你可以把数据库连接作为参数,然后将它们传送给ConnectionManager对象。请记住所有的连接管理都应该放在executeXXX()方法中。另外一种解决方案,也是一种更面向对象化的解决方案,是将一个连接工厂作为参数传递给SQLProcessor的构造函数。对于不同的连接工厂类型,我们需要不同的SQLProcessor对象。
ResultProcessor类的返回值:对象数组还是List?
为什么ResultProcessor接口中的process()方法返回的是对象数组呢?怎么不使用List类呢?这是由于在很多实际的应用中,SQL查询在大多数情况下值返回一行数据,在这种情况下,使用List对象会有些多余了。但是如果你确信SQL查询将返回多行结果,你可以使用List对象。
数据库操作异常
我们可以用多个自定义的数据库操作异常类来替代运行时发生的SQLException异常。最好在这些自定义的异常类时继承RuntimeException类,这样可以将这些异常进行集中处理。也许你会认为因该将异常处理放在发生异常的地方。但是我们设计这个的模型的目的之一是在JDBC应用程序开发中去掉或弱化异常处理的部分,只有使用RuntimeException我们才可能达到这个目的。