一、數據庫準備 html
要用JDBC操做數據庫,第一步固然是創建數據表:java
1
2
3
4
5
6
|
CREATE TABLE `user` (
`id`
int
(
11
) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(
45
) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`money`
double
DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
二、JDBC鏈接數據庫的基本步驟 mysql
JDBC鏈接數據庫包含如下幾個基本步驟:一、註冊驅動 ;二、創建鏈接(Connection);三、建立SQL語句(Statement);四、執行語句;五、處理執行結果(ResultSet);六、釋放資源。sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
static
void
test()
throws
SQLException{
// 1.註冊驅動
Class.forName(
"com.mysql.jdbc.Driver"
);
// 2.創建鏈接 url格式 - JDBC:子協議:子名稱//主機名:端口/數據庫名?屬性名=屬性值&…
// 3.建立語句
Statement st = conn.createStatement();
// 4.執行語句
ResultSet rs = st.executeQuery(
"select * from user"
);
// 5.處理結果
while
(rs.next()) {
System.out.println(rs.getObject(
1
) +
"\t"
+ rs.getObject(
2
) +
"\t"
+ rs.getObject(
3
) +
"\t"
+ rs.getObject(
4
));
}
// 6.釋放資源
rs.close();
st.close();
conn.close();
}
|
三、簡單的增刪改查 數據庫
第二節的代碼有一個問題,若是咱們在執行代碼時拋出異常,那麼Connection就沒法關閉了,因此咱們應該把關閉資源操做放入finally中,這樣就不管如何都會關閉這些數據庫鏈接資源。同時咱們還會擴展程序功能,上面的例子只是展現了一個查詢操做,接下來將會展現最經常使用的增、刪、改、查四個操做。首先介紹一個JdbcUtils類,該類會封裝數據庫鏈接步驟中的第一步、第二步及第六步操做,分別是註冊驅動,創建鏈接及釋放資源操做。編程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public
final
class
JdbcUtils {
static
{
try
{
Class.forName(
"com.mysql.jdbc.Driver"
);
}
catch
(ClassNotFoundException e) {
throw
new
ExceptionInInitializerError(e);
}
}
private
JdbcUtils() {
}
public
static
Connection getConnection()
throws
SQLException {
}
public
static
void
free(ResultSet rs, Statement st, Connection conn) {
try
{
if
(rs !=
null
)
rs.close();
}
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();
}
}
}
}
}
|
能夠看到,這個類的構造函數是一個私有構造函數,因此咱們將沒法建立這個類的實例。在靜態初始化域,咱們進行了註冊驅動操做,靜態初始化域只會在類加載的時候執行一次,這樣能夠保證只要加載了這個類,咱們會且僅會註冊一次驅動。而後getConnection()方法封裝了創建鏈接操做,free(rs, st, conn)方法封裝了釋放資源操做。接下來能夠看看如何使用JdbcUtils類進行增、刪、改、查操做:緩存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
//增長操做
void
create()
throws
SQLException {
Connection conn =
null
;
Statement st =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
int
i = st
.executeUpdate(
"insert into user(name,birthday, money) values ('name1', '1987-01-01', 400) "
);
System.out.println(
"i="
+ i);
}
finally
{
JdbcUtils.free(rs, st, conn);
}
}
//刪除操做
void
delete()
throws
SQLException {
Connection conn =
null
;
Statement st =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
int
i = st.executeUpdate(
"delete from user where id>4"
);
System.out.println(
"i="
+ i);
}
finally
{
JdbcUtils.free(rs, st, conn);
}
}
//修改操做
void
update()
throws
SQLException {
Connection conn =
null
;
Statement st =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
int
i = st.executeUpdate(
"update user set money=money+10 "
);
System.out.println(
"i="
+ i);
}
finally
{
JdbcUtils.free(rs, st, conn);
}
}
//查詢操做
void
read()
throws
SQLException {
Connection conn =
null
;
Statement st =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
rs = st.executeQuery(
"select id, name, money, birthday from user"
);
while
(rs.next()) {
System.out.println(rs.getObject(
"id"
) +
"\t"
+ rs.getObject(
"name"
) +
"\t"
+ rs.getObject(
"birthday"
) +
"\t"
+ rs.getObject(
"money"
));
}
}
finally
{
JdbcUtils.free(rs, st, conn);
}
}
|
四、面向對象封裝增刪改查 安全
第三節的例子只是爲了展現如何使用JDBC進行增刪改查操做,在項目中真正使用時,咱們是不會像上面的例子這樣簡單使用的,Java是面向對象的,因此咱們通常會使用面向對象的思想對操做進行封裝。首先,其實對於數據表每一條數據,咱們均可以認爲它是一個對象實例,例如此例中咱們定義的數據表User有id,name,birthday和money四個屬性,對應的咱們能夠建立User類以下:app
1
2
3
4
5
6
7
8
|
public
class
User {
private
int
id;
private
String name;
private
Date birthday;
private
float
money;
//getters and setters
}
|
按照"面向接口編程而非面向實現編程"的原則,咱們能夠定義數據表操做的接口以下:框架
1
2
3
4
5
6
|
public
interface
UserDao {
public
void
addUser(User user);
public
User getUser(
int
userId);
public
void
update(User user);
public
void
delete(User user);
}
|
而後咱們使用JDBC方式實現這個接口以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
public
class
UserDaoJdbcImpl
implements
UserDao {
public
void
addUser(User user) {
Connection conn =
null
;
PreparedStatement ps =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
String sql =
"insert into user(name,birthday, money) values (?,?,?) "
;
ps = conn.prepareStatement(sql);
ps.setString(
1
, user.getName());
ps.setDate(
2
,
new
java.sql.Date(user.getBirthday().getTime()));
ps.setFloat(
3
, user.getMoney());
ps.executeUpdate();
}
catch
(SQLException e) {
throw
new
RuntimeException(e.getMessage(), e);
}
finally
{
JdbcUtils.free(rs, ps, conn);
}
}
public
void
delete(User user) {
Connection conn =
null
;
Statement st =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql =
"delete from user where id="
+ user.getId();
st.executeUpdate(sql);
}
catch
(SQLException e) {
throw
new
RuntimeException(e.getMessage(), e);
}
finally
{
JdbcUtils.free(rs, st, conn);
}
}
public
User getUser(
int
userId) {
Connection conn =
null
;
PreparedStatement ps =
null
;
ResultSet rs =
null
;
User user =
null
;
try
{
conn = JdbcUtils.getConnection();
String sql =
"select id, name, money, birthday from user where id=?"
;
ps = conn.prepareStatement(sql);
ps.setInt(
1
, userId);
rs = ps.executeQuery();
while
(rs.next()) {
user =
new
User();
user.setId(rs.getInt(
"id"
));
user.setName(rs.getString(
"name"
));
user.setMoney(rs.getFloat(
"money"
));
user.setBirthday(rs.getDate(
"birthday"
));
}
}
catch
(SQLException e) {
throw
new
RuntimeException(e.getMessage(), e);
}
finally
{
JdbcUtils.free(rs, ps, conn);
}
return
user;
}
public
void
update(User user) {
Connection conn =
null
;
PreparedStatement ps =
null
;
ResultSet rs =
null
;
try
{
conn = JdbcUtils.getConnection();
String sql =
"update user set name=?, birthday=?, money=? where id=? "
;
ps = conn.prepareStatement(sql);
ps.setString(
1
, user.getName());
ps.setDate(
2
,
new
java.sql.Date(user.getBirthday().getTime()));
ps.setFloat(
3
, user.getMoney());
ps.setInt(
4
, user.getId());
ps.executeUpdate();
}
catch
(SQLException e) {
throw
new
RuntimeException(e.getMessage(), e);
}
finally
{
JdbcUtils.free(rs, ps, conn);
}
}
}
|
能夠看到,真正核心的代碼其實和第二節的代碼很相像,可是按照這種風格寫的代碼擴展性更好,若是哪一天咱們不打算使用JDBC,而改用Hibernate鏈接數據庫,使用接口編程只需修改實現,不須要修改其餘部分,大大減少了修改難度。
五、傳入sql執行
須要說明的是,上面的代碼使用了PreparedStatement對象,PrepareStatement是預編譯的Statement對象,它在建立的時候就會把sql的大致框架搭建起來,把一些變量用佔位符表示,使用時,咱們再設置這些佔位符的值。PrepareStatement最大的特色是能夠防止sql注入,更安全,因此再須要拼接用戶輸入的場景,推薦使用PrepareStatement。
第四節代碼的編碼風格相似Hibernate,Hibernate的不少操做都是須要傳入對象的,可是這種傳遞對象的方式靈活性不高,例如update()方法,咱們把User對象上的全部屬性都更新了,可是可能咱們只想更新birthday一個屬性,更新其餘屬性有點多餘,因此更好的方法應該是傳入sql語句,而不是一個User對象。再仔細觀察,咱們發現,其實咱們最終只是調用了Statement上的兩個方法,分別是executeUpdate和executeQuery兩個方法。因此咱們能夠把上面的增刪改查修改成以下形式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
public
class
UserDaoUtils {
private
UserDaoUtils(){
}
static
User executeQuery(String sql, Object[] params)
throws
SQLException {
Connection conn =
null
;
PreparedStatement ps =
null
;
ResultSet rs =
null
;
User user =
null
;
try
{
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement(sql);
for
(
int
i =
1
; i <= params.length; i++) {
ps.setObject(i, params[i -
1
]);
}
rs = ps.executeQuery();
while
(rs.next()) {
user =
new
User();
user.setId(rs.getInt(
"id"
));
user.setBirthday(rs.getDate(
"birthday"
));
user.setMoney(rs.getFloat(
"money"
));
user.setName(rs.getString(
"name"
));
}
}
finally
{
JdbcUtils.free(rs, ps, conn);
}
return
user;
}
static
int
executeUpdate(String sql, Object[] params)
throws
SQLException {
Connection conn =
null
;
PreparedStatement ps =
null
;
int
rs =
0
;
try
{
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement(sql);
for
(
int
i =
1
; i <= params.length; i++) {
ps.setObject(i, params[i -
1
]);
}
rs = ps.executeUpdate();
}
finally
{
JdbcUtils.free(
null
, ps, conn);
}
return
rs;
}
}
public
class
UserDaoJdbcImpl2
implements
UserDao{
@Override
public
void
addUser(User user) {
try
{
UserDaoUtils.executeUpdate(
"insert into user(name,birthday, money) values (?,?,?)"
,
new
Object[]{user.getName(), user.getBirthday(), user.getMoney()});
}
catch
(SQLException e) {
e.printStackTrace();
}
}
@Override
public
User getUser(
int
userId) {
User user =
null
;
try
{
user = UserDaoUtils.executeQuery(
"select id, name, money, birthday from user where id=?"
,
new
Object[]{userId});
}
catch
(SQLException e) {
e.printStackTrace();
}
return
user;
}
@Override
public
void
update(User user) {
try
{
UserDaoUtils.executeUpdate(
"update user set name=?, birthday=?, money=? where id=?"
,
new
Object[]{user.getName(), user.getBirthday(), user.getMoney(), user.getId()});
}
catch
(SQLException e) {
e.printStackTrace();
}
}
@Override
public
void
delete(User user) {
try
{
UserDaoUtils.executeUpdate(
"delete from user where id=?"
,
new
Object[]{user.getId()});
}
catch
(SQLException e) {
e.printStackTrace();
}
}
}
|
首先咱們定義了一個UserDaoUtils對象,該對象包含兩個方法,分別是executeQuery()和executeUpdate()方法,這兩個方法均包含兩個參數,分別是sql語句及sql語句的參數。而後咱們定義了UserDaoJdbcImpl2類,該類使用UserDaoUtils實現了UserDao接口,相較於UserDaoJdbcImpl簡化了不少。
六、利用結果集元數據封裝對象
上面的UserDaoJdbcImpl2和UserDaoUtils的代碼都已經很簡潔了,可是有個問題,若是咱們想封裝其餘對象的JDBC操做,那麼咱們將不得不從新定義一對Utils和Impl,這個實際上是重複勞動,那麼咱們有沒有什麼方法能夠避免這些重複勞動呢?Impl對象是必須定義的,由於咱們須要實現不一樣的對象,若是想少定義一些對象,那麼就只能不定義Utils對象。查看UserUtils的exectueQuery()和executeUpdate()方法,發現只有executeQuery()方法是與User對象耦合的,並且耦合部分只有封裝結果集的部分,咱們能夠把這一部分代碼抽象成一個接口,讓調用方傳入,這樣就能夠避免這部分耦合,因此定義接口以下:
1
2
3
|
public
interface
RowMapper {
public
Object mapRow(ResultSet rs)
throws
SQLException;
}
|
而後咱們修改第四節的UserDaoUtils對象以下,並重命名爲MyJdbcTemplate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public
class
MyJdbcTemplate {
private
MyJdbcTemplate(){}
public
static
Object executeQuery(String sql, Object[] args, RowMapper rowMapper) {
Connection conn =
|