JDBC:
虽然由于快节奏的开发,编程速度的追求,越爱越多的MVC框架出现,比如持久层的hibernate,
mybatis等等,他们对Dao层的支持都很强大,既快速,又简便。但是他们的底层同样是使用了JDBC,
为了追求高速简便,我们可以不使用JDBC,但一定要了解JDBC。了解JDBC也有助于学习其他持久层框架。
java和数据库交互需要中间程序作为中转。在很早以前,数据库厂商还没有一套统一的API作为
java语言和数据库的接口,开发程序是一件很头疼的事。对不同的数据库需要写不同的程序来作为交互。
java访问-----oracle程序-----oracle数据库
java访问------mysql程序------mysql数据库
java访问-------db2程序--------db2数据库
那到底什么是JDBC呢:
JDBC是代表Java数据库连接,这对java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API
有了JDBC开发就统一了很多。
java访问-----JDBC程序-----oracle数据库
java访问------JDBC 程序------mysql数据库
java访问------JDBC 程序--------db2数据库
以下简介JDBC针对oracl和mysql数据库。
oracle提供的jdbc接口的实现类-------ojdbc5.jar------jdk5.0 ojdbc6.jar-----jdk6.0
mysql提供的jdbc接口实现类--------mysql-connector-java-6.0.6
如果要开发JDBC程序,必然少不了这些jar包。下载可以去官网
JDBC常用API简介:
java.sql.Connection:代表一个数据库连接;
java.sql.Statement:发送sql语句1至数据库;(发送sql的工具)
DriverManager:(类) 管理多个数据库驱动类
java.sql.ResultSet:结果集,存放查询语句执行后返回的数据结果
下图1.0为JDBC访问数据库流程
图1.0
JDBC开发步骤
1: 加载驱动
如果是oracle数据库:
将oracle ojdbc.jar 复制到项目里,点击项目,右键build path --add to buidpath
Class.forName("oracle.jdbc.OracleDriver");
如果是mysql数据库:
将mysql mysql-connector-java-6.0.6-bin 复制到项目里,build path --add to buidpath
Class.forName("com.mysql.jdbc.Driver") ;
2:连接数据库
user:root----数据库用户名
password:root------数据库密码
url: oracle ----
协议
:
子协议
:thin:@ip:端口:SID(SID数据库的实例名)。
eg:
String url = "jdbc:oracle:thin:@localhost:1521:XE";
url:mysql ------
协议:子协议://ip:端口:
数据库名
eg:String
url
=
"jdbc:mysql://localhost:3306/zdx"
;
3:准备sql
sql字符串中不能有分号;
4:Statement将sql发送至数据库
int i= executeUpdate(sql); //返回受影响的行数
5:如果是查询语句,返回结果集,处理ResultSet。
6:关闭连接,释放资源,按照先打开后关闭的原则。
以上6步骤时开发JDBC程序的标准步骤。
ResultSet结果集简介及使用方法:
ResultSet结果集:存放查询结果数据。
ResultSet rs = Statement.
executeQuery();
rs指针初始位置在第一行数据之前
boolean rs.next(): ,
将rs的指针向下移动一位,当前行无数据返回false,有数据返回true,并获得查询数据。
提供一组getXXX(int列序号或String列名)方法用于取得数据
以上是JDBC简介,下面是JDBC例子,例子才是王道。
JDBC1.0版本------是1.0版本。后面还会有更优秀的版本娓娓道来!
JDBC_ResultSet.java
public
class
JDBC_ResultSet {
public
static
void
main(String[]
args
)
throws
ClassNotFoundException, SQLException {
//1:加载驱动
//Class.forName("oracle.jdbc.OracleDriver");
Class.forName(
"com.mysql.jdbc.Driver"
);
//2:获得数据库连接
String
user
=
"root"
;
String
password
=
"root"
;
//String url = "jdbc:oracle:thin:@localhost:1521:XE";
String
url
=
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
;
Connection
conn
= DriverManager.getConnection(
url
,
user
,
password
);
//3:准备sql
String
sql
=
"select * from student"
;
//4:创建Statement,发送sql
Statement
stm
=
conn
.createStatement();
//获的statement对象
//5:如果是查询,处理结果集
ResultSet
rs
=
stm
.executeQuery(
sql
);
while
(
rs
.next()){
int
id
=
rs
.getInt(1);
String
name
=
rs
.getString(2);
int
age
=
rs
.getInt(3);
String
phone
=
rs
.getString(4);
System.
out
.println(
id
+
"---"
+
name
+
"---"
+
age
+
"---"
+
phone
);
}
//6:关闭连接,释放资源,原则,先打开后关闭
stm
.close();
conn
.close();
}
}
到了此处,我们的JDBC1.0已经写完了,但是为什么说是1.0呢,因为这个版本有一个很大的漏洞。
没错,那就是——依赖注入问题。
=============================华丽丽的分割线==============================
下面我们可以看这个例子
JDBC_Statement.java
public
class
JDBC_Statement {
public
static
void
main(String[]
args
)
throws
Exception {
Scanner
sc
=
new
Scanner(System.
in
);
System.
out
.println(
"请输入卡号"
);
String
card_id
=
sc
.nextLine();
System.
out
.println(
"请输入密码"
);
String
pwd
=
sc
.nextLine();
//1:加载驱动
Class.forName(
"com.mysql.jdbc.Driver"
);
//2:获得数据库连接
String
user
=
"root"
;
String
password
=
"root"
;
String
url
=
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
;
Connection
conn
= DriverManager.getConnection(
url
,
user
,
password
);
//3:准备sql
String
sql
=
"select * from account where card_id ="
+
card_id
+
" and password = '"
+
pwd
+
"'"
;
System.
out
.println(
sql
);
//4:创建Statement,发送sql
Statement
stm
=
conn
.createStatement();
//获的statement对象
//5:如果是查询,处理结果集
ResultSet
rs
=
stm
.executeQuery(
sql
);
if
(
rs
.next()){
System.
out
.println(
"可以取钱了"
);
}
else
{
System.
out
.println(
"密码账户不正确"
);
}
//6:关闭连接,释放资源,原则,先打开后关闭
stm
.close();
conn
.close();
}
}
这是一个银行取钱的例子,如代码所写。
数据库有这样一张account表,数据和字段如下:
现在在控制台输入卡号密码,卡号密码正确即可取钱,
错误即提示账户不正确。
现在控制台输入正确卡号密码
然后,我输入错误的卡号密码竟然也可以取钱!输入错误的卡号 “123 or 1=1 -- ”密码 "zxc"结果如下:
可见依然能访问数据库,这对于银行可是致命性错误,密码错误还可以取钱。
这就是依赖注入引起的著名错误。
依靠人为输入破坏sql结构.
select * from account where id = 1001 or 1=1 -- and password = 'xxx'
这条sql里,其中--是sql里的注释 or 1=1 永远为真并且还是or连接,
所以这条sql只执行到 or 1=1 ,1=1又是恒等。所以跳过了密码。
为了解决这个问题,我们就要说PreparedStatement!
PreparedStatement简介及使用
PreparedStatement构建动态SQL,通过PreparedStatement执行SQL语句,解决注入攻击。
PreparedStatement是Statement的子接口。
通过连接创建PreparedStatement,创建时将SQL语句中发生变化的部分用占位符“?“ 代替。
功能:和statement一样,发送sql语句。
但是执行多个同构sql效率高。同构sql能省去①②③步骤。
使用步骤:
1.创建pstm
String sql = "select * from account where card_id = ? and password = ?"
PreparedStateement pstm = conn.prepareStatement(sql);
①验证权限
②验证语法
③将sql转换内部指令
2.绑定参数
pstm.setInt(1,值);
pstm.setString(2,值);
3.发送绑定参数至DB数据库
pstm.executeUpdate();//曾删改
pstm.executedQuery();//查询
④执行内部指令操作数据库
mysql内部执行sql步骤:
①验证权限
②验证语法
③将sql转换内部指令
④执行内部指令操作数据库
使用PareparedStatement解决注入攻击问题后的代码如下:
JDBC2.0版本 是2.0版本>_< 。后面还会有更优秀的版本娓娓道来!
JDBC_PreparedStatement.java
public
class
JDBC_PreparedStatement {
public
static
void
main(String[]
args
)
throws
Exception {
Scanner
sc
=
new
Scanner(System.
in
);
System.
out
.println(
"请输入卡号"
);
String
card_id
=
sc
.nextLine();
System.
out
.println(
"请输入密码"
);
String
pwd
=
sc
.nextLine();
//1:加载驱动
//Class.forName("com.mysql.jdbc.Driver");
//2:获得数据库连接
String
user
=
"root"
;
String
password
=
"root"
;
String
url
=
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
;
Connection
conn
= DriverManager.getConnection(
url
,
user
,
password
);
//3:准备sql
String
sql
=
"select * from account where card_id =? and password =?"
;
PreparedStatement
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setInt(1, Integer.valueOf(
card_id
));
pstm
.setString(2,
pwd
);
//5:如果是查询,处理结果集
ResultSet
rs
=
pstm
.executeQuery();
if
(
rs
.next()){
System.
out
.println(
"可以取钱了"
);
}
else
{
System.
out
.println(
"密码账户不正确"
);
}
//6:关闭连接,释放资源,原则,先打开后关闭
rs
.close();
pstm
.close();
conn
.close();
}
}
现在用PreparedStatement程序执行,输入卡号,密码
出现异常,不会再出现密码错误却取钱的问题了!!!
总结对比一下 Statement 和PreparedStatement
|
Statement
|
PreparedStatement
|
关系
|
父接口
|
子接口
|
安全
|
存在注入隐患
|
解决sql注入
|
效率
|
执行异构sql快
|
执行同构sql快
|
至此JDBC2.0结束。JDBC基本内容结束,以下是JDBC进阶内容。和优化版JDBC例子。
=================================华丽丽的分割线===========================
封装数据访问对象
1:通过分析总结,所有对数据库表的操作都可以总结为通过JDBC对表的增删改查,为了减少冗余代码,
使得每次操作表时,不必都写JDBC程序,所以将对一张表的所有数据访功能,封装在数据访问对象
(Data Access Object)中,方便调用。
2:为了方便数据传输,往往会将java程序中所有相关操作的零散字段值,封装成一个实体对象--entity。
实体封装原则:
表----实体类
字段---属性
实现序列化
提供set,get方法。
以下代码就是利用Dao数据访问对象写出的JDBC程序,利用封装简化了JDBC开发步骤。
试想一下,如果,有10个添加,20个删除的请求,还像JDBC1.0,2.0版本那样写的话,要写10遍,20遍大量相似代码,
基本一致的代码。这不仅耗时耗力,也不符合JAVA三大特性。所以,利用Dao数据访问层,将对表的常用操作,
封装成方法,这样再有10个添加或20个删除,我们只需要调用10次,20次这个封装好的添加或删除方法,
而不用再写那么多遍方法,大大简化了开发工作量。
以下是利用Dao思想实现的JDBC3.0是3.0版本后面还有更好的版本娓娓道来!>_<
JDBC_Dao.java
public
class
JDBC_Dao {
/**
* 向account表增加一条记录
*/
public
void
add(Account
account
){
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
try
{
//1加载驱动
Class.forName(
"com.mysql.jdbc.Driver"
);
//2创建连接
conn
= DriverManager.getConnection(
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
,
"root"
,
"root"
);
//3准备sql
String
sql
=
"insert into account values(?,?,?,?)"
;
//4创建Statement,发送sql
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
account
.getCardId());
pstm
.setString(2,
account
.getPassword());
pstm
.setDouble(3,
account
.getBalance());
pstm
.setString(4,
account
.getPhone());
int
i
=
pstm
.executeUpdate();
//5如果是查询的话,处理结果集
}
catch
(Exception
e
) {
e
.printStackTrace();
}
finally
{
//6释放资源
try
{
pstm
.close();
conn
.close();
}
catch
(SQLException
e
) {
e
.printStackTrace();
}
}
}
/**
* 向account表查询一条记录
*
@param
account
*
@return
*/
public
Account query(Account
account
){
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
ResultSet
rs
=
null
;
try
{
//1加载驱动
Class.forName(
"com.mysql.jdbc.Driver"
);
//2创建连接
conn
= DriverManager.getConnection(
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
,
"root"
,
"root"
);
//3准备sql
String
sql
=
"select * from account where "
+
"card_id = ? and password = ?"
;
//4创建Statement发送语句
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
account
.getCardId());
pstm
.setString(2,
account
.getPassword());
rs
=
pstm
.executeQuery();
//5处理结果集
while
(
rs
.next()) {
account
.setCardId(
rs
.getString(
"card_id"
));
account
.setPassword(
rs
.getString(
"password"
));
account
.setBalance(
rs
.getDouble(
"balance"
));
account
.setPhone(
rs
.getString(
"phone"
));
}
}
catch
(Exception
e
) {
e
.getStackTrace();
}
finally
{
//6释放资源
try
{
rs
.close();
pstm
.close();
conn
.close();
}
catch
(SQLException
e
) {
e
.printStackTrace();
}
}
return
account
;
}
}
以上代码依旧使用account表,需要再写一个实体对象用来承载数据,一个test类用来调用方法,
,这些最基础的,这里就不赘述了,不清楚的同学就要自行参阅java语言了。
如果我查询A卡,B卡,C卡,三张银行卡信息,按照JDBC2.0要写三个查询方法,现在,只需要把参数传递过去,
调用三次query()方法就好了!
总结:可见利用Dao数据访问层封装JDBC常用方法,可以大大简化方法步骤,不用重复写方法,只需重复调用。
这就是JDBC3.0版本
=============================华丽丽的分割线==============================
但是仔细的读者一定发现了,这里还存在不少缺陷,没错,我们还可以改进它。
在JDBC3.0版本里,可以发现,查询,添加方法,存在大量冗余代码,比如:
①同的加载驱动,
②相同的创建连接,
③相同的释放资源。
②相同的创建连接,
③相同的释放资源。
在上个版本的代码里我只写了添加查询方法,如果还有删除,修改,查询所有等方法呢,
没错这些方法,也存在相同的创建连接,释放链接。找见了问题,就好解决了。
那么解决的办法还是----封装。
我们可以尝试把1注册驱动,2创建连接,6释放资源,这三个步骤做成工具类-----JDBCutil
这样,我们在Dao层里面的JDBC方法,在遇到1,2,6等步骤时,不用再去写代码,只需调用封装好的工具即可。
没错程序员都是很懒得!
以下是JDBC4.0是4.0版本,后面还有更完善的版本娓娓道来!
public
class
JDBC_Util {
/**
*
@return
返回链接
*/
public
static
Connection getConnection()
throws
Exception {
Connection
conn
=
null
;
Class.forName(
"com.mysql.jdbc.Driver"
);
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
,
"root"
,
"root"
);
return
conn
;
}
/**
* 释放资源
*/
public
static
void
release(ResultSet
rs
,
Statement
stm
,
Connection
conn
){
try
{
if
(
rs
!=
null
){
rs
.close();}
if
(
stm
!=
null
){
stm
.close();}
if
(
conn
!=
null
){
conn
.close();}
}
catch
(SQLException
e
) {
e
.printStackTrace();
}
}
}
public
class
JDBC_Dao2 {
/**
* 向account表增加一条记录
*/
public
void
add(Account
account
){
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
try
{
conn
= JDBC_Util.getConnection();
//3准备sql
String
sql
=
"insert into account values(?,?,?,?)"
;
//4创建Statement,发送sql
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
account
.getCardId());
pstm
.setString(2,
account
.getPassword());
pstm
.setDouble(3,
account
.getBalance());
pstm
.setString(4,
account
.getPhone());
int
i
=
pstm
.executeUpdate();
//5如果是查询的话,处理结果集
}
catch
(Exception
e
) {
e
.printStackTrace();
}
finally
{
//6释放资源
JDBC_Util.release(
null
,
pstm
,
conn
);
}
}
/**
* 向account表查询一条记录
*
@param
account
*
@return
*/
public
Account query(Account
account
){
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
ResultSet
rs
=
null
;
try
{
conn
= JDBC_Util.getConnection();
//3准备sql
String
sql
=
"select * from account where "
+
"card_id = ? and password = ?"
;
//4创建Statement发送语句
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
account
.getCardId());
pstm
.setString(2,
account
.getPassword());
rs
=
pstm
.executeQuery();
//5处理结果集
while
(
rs
.next()) {
account
.setCardId(
rs
.getString(
"card_id"
));
account
.setPassword(
rs
.getString(
"password"
));
account
.setBalance(
rs
.getDouble(
"balance"
));
account
.setPhone(
rs
.getString(
"phone"
));
}
}
catch
(Exception
e
) {
e
.getStackTrace();
}
finally
{
//6释放资源
JDBC_Util.release(
rs
,
pstm
,
conn
);
}
return
account
;
}
}
细心地读者会发现在代码里原本创建连接和释放资源的位置都变成了方法调用。
conn
= JDBC_Util.getConnection();
JDBC_Util.release(
rs
,
pstm
,
conn
);
4.0版本通过工具类调用的方式进一步精简了代码,那么4.0版本还有没有缺陷了呢。
=============================华丽丽的分割线=================================
对于JDBC_Util.java来说,还有许多不足。
1:
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC"
,
"root"
,
"root"
);
比如创建连接的方式,是在代码里写死的用户名和密码,以及连接url,而java文件运行的时候,会编译成class文件
也就是说,JDBC_Util最终会是JDBC_Util.class文件,那么一旦数据库改变密码,用户名,或改换数据库,
整个文件还需要重新编译执行。对此,我们可以把经常变动的代码放到properties配置文件里去。
2:每次调用获得连接的方法都需要加载驱动,
Class.forName(
"com.mysql.jdbc.Driver"
);
调用10次则加载10次,大大浪费了JVM内存,其实对于加载驱动只需要加载一次,我们可以尝试把加载驱动放到
静态代码块里。静态代码块在类加载时执行,只执行一次。
properties配置文件简介及使用:
1: InputStream is = new FileInputStream("配置文件路径");
BufferedReader br = bew BufferedReader(new InputStramReader(is));
String as = br.readLine();
2:properties 是Map的实现类:
1:获得配置文件的输出流。
2:调用load(is);加载配置文件里的信息至Properties对象中。
下面的JDBC5.0版本是对JDBC_Util的改进。采用了静态代码块加读取配置文件的优化方案。
包括test.java,JDBC_Util2.java,JDBC_Dao2.java,properties文件,共四个。
JDBC_Uril2.java
public
class
JDBC_Util2 {
private
static
final
Properties
prop
=
new
Properties();
static
{
InputStream
is
=
null
;
try
{
is
= JDBC_Util2.
class
.getResourceAsStream(
"jdbc.properties"
);
prop
.load(
is
);
String
driverName
=
prop
.getProperty(
"driverName"
);
Class.forName(
driverName
);
}
catch
(Exception
e
) {
e
.printStackTrace();
}
}
/**
*
@return
返回链接
*/
public
static
Connection getConnection()
throws
Exception {
Connection
conn
=
null
;
String
user
=
prop
.getProperty(
"user"
);
String
password
=
prop
.getProperty(
"password"
);
String
url
=
prop
.getProperty(
"url"
);
conn
= DriverManager.getConnection(
url
,
user
,
password
);
return
conn
;
}
/**
* 释放资源
*/
public
static
void
release(ResultSet
rs
,
Statement
stm
,
Connection
conn
){
try
{
if
(
rs
!=
null
){
rs
.close();}
if
(
stm
!=
null
){
stm
.close();}
if
(
conn
!=
null
){
conn
.close();}
}
catch
(SQLException
e
) {
e
.printStackTrace();
}
}
}
JDBC_Dao2.java
public
class
JDBC_Dao2 {
/**
* 向account表增加一条记录
*/
public
void
add(Account
account
){
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
try
{
conn
= JDBC_Util2.getConnection();
//3准备sql
String
sql
=
"insert into account values(?,?,?,?)"
;
//4创建Statement,发送sql
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
account
.getCardId());
pstm
.setString(2,
account
.getPassword());
pstm
.setDouble(3,
account
.getBalance());
pstm
.setString(4,
account
.getPhone());
int
i
=
pstm
.executeUpdate();
//5如果是查询的话,处理结果集
}
catch
(Exception
e
) {
e
.printStackTrace();
}
finally
{
//6释放资源
JDBC_Util2.release(
null
,
pstm
,
conn
);
}
}
/**
* 向account表查询一条记录
*
@param
account
*
@return
*/
public
Account query(Account
account
){
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
ResultSet
rs
=
null
;
try
{
conn
= JDBC_Util2.getConnection();
//3准备sql
String
sql
=
"select * from account where "
+
"card_id = ? and password = ?"
;
//4创建Statement发送语句
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
account
.getCardId());
pstm
.setString(2,
account
.getPassword());
rs
=
pstm
.executeQuery();
//5处理结果集
while
(
rs
.next()) {
account
.setCardId(
rs
.getString(
"card_id"
));
account
.setPassword(
rs
.getString(
"password"
));
account
.setBalance(
rs
.getDouble(
"balance"
));
account
.setPhone(
rs
.getString(
"phone"
));
}
}
catch
(Exception
e
) {
e
.getStackTrace();
}
finally
{
//6释放资源
JDBC_Util2.release(
rs
,
pstm
,
conn
);
}
return
account
;
}
}
jdbc.properties文件
driverName=
com.mysql.jdbc.Driver
user=
root
password=
root
url=
jdbc:mysql://localhost:3306/zdx?serverTimezone=UTC
Test_Dao.java
public
class
Test_Dao {
public
static
void
main(String[]
args
) {
JDBC_Dao2
jd
=
new
JDBC_Dao2();
Account
account
=
new
Account(
"10004"
,
"44444"
,99,
"12345678900"
);
jd
.add(
account
);
}
}
解决了依赖注入,冗余代码,资源浪费等问题之后,JDBC5.0完毕。这个版本可以算得上是较为完善的版本了,但是还有瑕疵。
后面关于JDBC的问题涉及到事务,以及MVC模式,这里由于篇幅问题,无法细细详谈,但我会竟可能详细的写。
请小伙伴自行阅读有关oracle事务一章节,还有有关mvc编程思想书籍。
=================================华丽丽的分割线=========================
JDBC6.0最终版。
数据访问层Dao
业务逻辑层service
我们在Dao层中封装了对表的常用操作,增删改查。
我们在Util里封装了JDBCUtil工具类解决冗余问题。
现在我们有一个银行转账问题:
1.根据卡号,密码,先查询
2.转出账户再余额足够的情况下,减去转出资金。
3转入账户添加转入资金。
对于2,3步骤我们因当把他们看作是一个事务,事务的原子性,一致性,隔离性。要求
要么一起成功,要么一起不成功。当然从现实考虑,也确实应当这样,如果在2,3步骤之间失败了
就回退。
这整个转账逻辑,我们可以称之为业务。所以针对软件的三层结构。我们把这部分称为-----业务层(service)
在service层调用Dao层的方法。
而在service层里的连接对象和Dao层里的连接对象不是同一个对象,这就会造成混乱,打破事务的一致性。
从而导致,转出钱减去了,转入账户没加钱。
Service方法中的事务控制失败。
原因:service控制事务时过的conn和DAO访问数据库使用的conn时两个不同对象
解决:
保证service和Dao使用同一个conn对象
1:通过参数传递。
2:每个线程对象有一个Map属性,可以存储数据
在service获得conn,将conn放入当前Thread对象,在DAO访问数据库时取时,从Thread取出conn
ThreadLocal简介及使用:
操作当前线程对象中的一小块空间。
1创建ThreadLocal对象 ThreadLocal<Connection> tdl = new ThreadLocal<Connection>();
2tdl.set(conn);将conn添加到当前线程。
3tdl.get();获得当前线程中的数据
4tdl.remove()移除当前线程里的数据
话不多说我们来看代码有三个文件 JDBC_Util3.java JDBC_Dao3.java JDBC_Service.java
JDBC_Util3.java
public
class
JDBC_Util3 {
private
static
final
Properties
prop
=
new
Properties();
private
static
final
ThreadLocal<Connection>
tdl
=
new
ThreadLocal<Connection>();
static
{
InputStream
is
=
null
;
try
{
is
= JDBC_Util3.
class
.getResourceAsStream(
"jdbc.properties"
);
prop
.load(
is
);
String
driverName
=
prop
.getProperty(
"driverName"
);
Class.forName(
driverName
);
}
catch
(Exception
e
) {
e
.printStackTrace();
}
}
/**
*
@return
返回链接
*/
public
static
Connection getConnection()
throws
Exception {
Connection
conn
=
null
;
conn
=
tdl
.get();
//获得当前线程连接
if
(
conn
==
null
){
//说明当前线程没有conn
String
user
=
prop
.getProperty(
"user"
);
String
password
=
prop
.getProperty(
"password"
);
String
url
=
prop
.getProperty(
"url"
);
conn
= DriverManager.getConnection(
url
,
user
,
password
);
//将当 conn存入线程
tdl
.set(
conn
);
}
return
conn
;
}
/**
* 释放资源
*/
public
static
void
release(ResultSet
rs
,Statement
stm
,Connection
conn
){
try
{
if
(
rs
!=
null
){
rs
.close();}
if
(
stm
!=
null
){
stm
.close();}
if
(
conn
!=
null
){
conn
.close();
tdl
.remove();
//移除当前线程对象中的conn
}
}
catch
(SQLException
e
) {
e
.printStackTrace();
}
}
}
JDBC_Dao3.java
public
class
JDBC_Dao3 {
/**
* 更新账户
*
@param
toAcc
*/
public
void
upDateAccount(Account
toAcc
) {
Connection
conn
=
null
;
PreparedStatement
pstm
=
null
;
try
{
conn
= JDBC_Util3.getConnection();
String
sql
=
"update account set card_id=?,"
+
"password=?,balance=?,phone=? where card_id=?"
;
pstm
=
conn
.prepareStatement(
sql
);
pstm
.setString(1,
toAcc
.getCardId());
pstm
.setString(2,
toAcc
.getPassword());
pstm
.setDouble(3,
toAcc
.getBalance());
pstm
.setString(4,
toAcc
.getPhone());
pstm
.setString(5,
toAcc
.getCardId());
int
i
=
pstm
.executeUpdate();
}
catch
(Exception
e
) {
e
.printStackTrace();
}
finally
{
JDBC_Util.release(
null
,
pstm
,
null
);
}
}
/**
* 查询账户
*
@param
toCardId
*
@return
*/
public
Account queryAccount(Integer
toCardId
) {
Connection
conn
=
null
;
Statement
stm
=
null
;
ResultSet
rs
=
null
;
Account
acc
=
null
;
try
{
conn
= JDBC_Util3.getConnection();
stm
=
conn
.createStatement();
String
sql
=
"select * from account where card_id = "
+
toCardId
;
System.
out
.println(
sql
);
rs
=
stm
.executeQuery(
sql
);
while
(
rs
.next()) {
acc
=
new
Account();
acc
.setCardId(
rs
.getString(
"card_id"
));
acc
.setPassword(
rs
.getString(
"password"
));
acc
.setBalance(
rs
.getDouble(
"balance"
));
acc
.setPhone(
rs
.getString(
"phone"
));
}
}
catch
(Exception
e
) {
}
finally
{
JDBC_Util.release(
rs
,
stm
,
null
);
}
return
acc
;
}
}
JDBC_Service.java
public
class
JDBC_Service {
public
void
transfer(Integer
fromCardId
,String
password
,
Integer
toCardId
,Double
money
){
Connection
conn
=
null
;
try
{
conn
= JDBC_Util2.getConnection();
//事务自动提交关闭
conn
.setAutoCommit(
false
);
JDBC_Dao3
dao
=
new
JDBC_Dao3();
//验证卡号是否存在
Account
fromAcc
=
dao
.queryAccount(
fromCardId
);
if
(
fromAcc
==
null
){
throw
new
RuntimeException(
"卡号不存在"
);
}
//验证密码是否正确
if
(
null
==
password
||!
password
.equals(
fromAcc
.getPassword())){
throw
new
RuntimeException(
"密码错误"
);
}
//验证余额
if
(
fromAcc
.getBalance()<
money
){
throw
new
RuntimeException(
"余额不足"
);
}
//转出账户更新
fromAcc
.setBalance(
fromAcc
.getBalance()-
money
);
dao
.upDateAccount(
fromAcc
);
Account
toAcc
=
dao
.queryAccount(
toCardId
);
//验证到账卡号
if
(
toAcc
==
null
){
throw
new
RuntimeException(
"到账卡号不存在"
);
}
//转入账户更新
toAcc
.setBalance(
toAcc
.getBalance()+
money
);
dao
.upDateAccount(
toAcc
);
//提交事务
conn
.commit();
}
catch
(Exception
e
){
e
.printStackTrace();
try
{
conn
.rollback();
}
catch
(SQLException
e1
) {
e1
.printStackTrace();
}
}
finally
{
JDBC_Util.release(
null
,
null
,
conn
);
}
}
}
最后main函数测试
JDBC_Service
sv
=
new
JDBC_Service();
sv
.transfer(10002,
"123321"
, 10004, 30.00);
转账成功:1004:99+30 10002:99-30
=========================华丽丽的终结=====================================
总结:虽然由于快节奏的开发,编程速度的追求,越爱越多的MVC框架出现,比如持久层的hibernate,
mybatis等等,他们对Dao层的支持都很强大,既 快速,又简便。但是他们的底层同样是使用了JDBC,
为了追求高速简便,我们可以不使用JDBC,但一定要了解JDBC。了解JDBC也有助于学习其他持久层框架。
以上就是我对JDBC全部心得。
限于文章篇幅原因,这里仅仅介绍冰山一角。由于笔者的水平有限,编写时间也很仓促,
文中难免会出现一些错误或者不准确的地方,不妥之处恳请读者批评指正。