什么是JDBC
JDBC(Java Database Connectivity),即Java數據庫連接,是一種用于執行SQL語句的Java API,可以為多種關系數據庫提供同一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準,根據這種基準可以構建更高級的工具和接口,使數據庫開發人員能夠編寫數據庫應用程序。總而言之,JDBC做了三件事:
1、與數據庫建立連接
2、發送操作數據庫的語句
3、處理結果
JDBC簡單示例
下面的代碼演示了如何利用JDBC從數據庫中查詢若干條符合要求的數據出來,使用的數據庫是MySql。
1、建立一個數據庫和一張表,我的習慣是在CLASSPATH底下建立一個.sql的文件用于存放sql語句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
create database school; use school; create table student ( studentId int primary key auto_increment not null , studentName varchar( 10 ) not null , studentAge int , studentPhone varchar( 15 ) ) insert into student values( null , 'Betty' , '20' , '00000000' ); insert into student values( null , 'Jerry' , '18' , '11111111' ); insert into student values( null , 'Betty' , '21' , '22222222' ); insert into student values( null , 'Steve' , '27' , '33333333' ); insert into student values( null , 'James' , '22' , '44444444' ); commit; |
2、建立一個.properties文件用于存儲MySql連接的幾個屬性。為什么要建立.properties而不在代碼里面寫死,由于這個并不是Java設計模式的分類,就不細講了,只需要記住:從設計的角度看,把內容寫在配置文件中永遠好過把內容寫死在代碼中。
1
2
3
4
|
mysqlpackage=com.mysql.jdbc.Driver mysqlurl=jdbc:mysql: //localhost:3306/school?useUnicode=true&characterEncoding=utf-8 mysqlname=root mysqlpassword=root |
3、根據表字段建立實體類
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
|
public class Student { private int studentId; private String studentName; private int studentAge; private String studentPhone; public Student( int studentId, String studentName, int studentAge, String studentPhone) { this .studentId = studentId; this .studentName = studentName; this .studentAge = studentAge; this .studentPhone = studentPhone; } public int getStudentId() { return studentId; } public String getStudentName() { return studentName; } public int getStudentAge() { return studentAge; } public String getStudentPhone() { return studentPhone; } public String toString() { return "studentId = " + studentId + ", studentName = " + studentName + ", studentAge = " + studentAge + ", studentPhone = " + studentPhone; } } |
4、寫一個DBConnection類專門用于向外提供數據庫連接。我這里用了MySql,所以只有一個mysqlConnection,如果還用到了Oracle,當然還可以向外提供一個oracleConnection。把這些連接設為全局的可能有人會想是否會有線程安全問題,這是一個很好的問題。那因為我們只從Connection里面讀取一個PreparedStatement出來,而不會去寫它,只讀不修改,是不會引發線程安全問題的。另外把Connection設置為static的保證了Connection在內存中只有一份,不會占多大資源,每次使用完不調用close()方法去關閉它也沒事。
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
|
public class DBConnection { private static Properties properties = new Properties(); static { /** 要從CLASSPATH下取.properties文件,因此要加"/" */ InputStream is = DBConnection. class .getResourceAsStream( "/db.properties" ); try { properties.load(is); } catch (IOException e) { e.printStackTrace(); } } /** 這個mysqlConnection只是為了用來從里面讀一個PreparedStatement,不會往里面寫數據,因此沒有線程安全問題,可以作為一個全局變量 */ public static Connection mysqlConnection = getConnection(); public static Connection getConnection() { Connection con = null ; try { Class.forName((String)properties.getProperty( "mysqlpackage" )); con = DriverManager.getConnection((String)properties.getProperty( "mysqlurl" ), (String)properties.getProperty( "mysqlname" ), (String)properties.getProperty( "mysqlpassword" )); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return con; } } |
5、建立一個工具類,用來寫各種方法,專門和數據庫進行交互。這種工具類最好搞成單例的,這樣就不用每次去new出來了(實際上new出來也沒看出來會有什么好處),節省資源
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
|
package com.xrq.test11; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; public class StudentManager { private static StudentManager instance = new StudentManager(); private StudentManager() { } public static StudentManager getInstance() { return instance; } public List<Student> querySomeStudents(String studentName) throws Exception { List<Student> studentList = new ArrayList<Student>(); Connection connection = DBConnection.mysqlConnection; PreparedStatement ps = connection.prepareStatement( "select * from student where studentName = ?" ); ps.setString( 1 , studentName); ResultSet rs = ps.executeQuery(); Student student = null ; while (rs.next()) { student = new Student(rs.getInt( 1 ), rs.getString( 2 ), rs.getInt( 3 ), rs.getString( 4 )); studentList.add(student); } ps.close(); rs.close(); return studentList; } } |
6、寫個main函數去調用一下
1
2
3
4
|
List<Student> studentList = new ArrayList<Student>(); studentList = StudentManager.getInstance().querySomeStudents( "Betty" ); for (Student student : studentList) System.out.println(student); |
7、看一下運行結果,和數據庫里面的一樣,成功
1
2
|
studentId = 1 , studentName = Betty, studentAge = 20 , studentPhone = 00000000 studentId = 3 , studentName = Betty, studentAge = 21 , studentPhone = 22222222 |
為什么要使用占位符"?"
看一下第5點,大家一定注意到了,寫sql語句的時候用了"?"占位符,當然有美化代碼的因素,不用占位符就要在括號里寫"+"來拼接參數,如果要拼接的參數一多,代碼肯定不好看,可讀性不強。但是除了這個原因,還有另外一個重要的原因,就是避免一個安全問題。假設我們不用占位符寫sql語句,那"querySomeStudents(String name) throws Exception"方法就要這么寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public List<Student> querySomeStudents(String studentName) throws Exception { List<Student> studentList = new ArrayList<Student>(); Connection connection = DBConnection.mysqlConnection; PreparedStatement ps = connection.prepareStatement( "select * from student where studentName = '" + studentName + "'" ); ResultSet rs = ps.executeQuery(); Student student = null ; while (rs.next()) { student = new Student(rs.getInt( 1 ), rs.getString( 2 ), rs.getInt( 3 ), rs.getString( 4 )); studentList.add(student); } ps.close(); rs.close(); return studentList; } |
上面的main函數一樣可以獲取到兩條數據,但是問題來了,如果我這么調用呢:
1
2
3
4
5
6
7
|
public static void main(String[] args) throws Exception { List<Student> studentList = new ArrayList<Student>(); studentList = StudentManager.getInstance().querySomeStudents( "' or '1' = '1" ); for (Student student : studentList) System.out.println(student); } |
看下運行結果:
1
2
3
4
5
|
studentId = 1, studentName = Betty, studentAge = 20, studentPhone = 00000000 studentId = 2, studentName = Jerry, studentAge = 18, studentPhone = 11111111 studentId = 3, studentName = Betty, studentAge = 21, studentPhone = 22222222 studentId = 4, studentName = Steve, studentAge = 27, studentPhone = 33333333 studentId = 5, studentName = James, studentAge = 22, studentPhone = 44444444 |
為什么?看下拼接之后的sql語句就知道了:
select * from student where studentName = '' or '1' = '1'
'1'='1'永遠成立,所以前面的查詢條件是什么都沒用。這種問題是有應用場景的,不是隨便寫一下。Java越來越多的用在Web上,既然是Web,那么查詢的時候有一種情況就是用戶輸入一個條件,后臺獲取到查詢條件,拼接sql語句查數據庫,有經驗的用戶完全可以輸入一個"‘'' or '1' = '1",這樣就拿到了庫里面的所有數據了。
JDBC事物
談數據庫必然離不開事物,事物簡單說就是"要么一起成功,要么一起失敗"。那簡單往前面的StudentManager里面寫一個插入學生信息的方法:
1
2
3
4
5
6
7
8
9
10
11
12
|
public void addStudent(String studentName, int studentAge, String studentPhone) throws Exception { Connection connection = DBConnection.mysqlConnection; PreparedStatement ps = connection.prepareStatement( "insert into student values(null,?,?,?)" ); ps.setString( 1 , studentName); ps.setInt( 2 , studentAge); ps.setString( 3 , studentPhone); if (ps.executeUpdate() > 0 ) System.out.println( "添加學生信息成功" ); else System.out.println( "添加學生信息失敗" ); } |
1
2
3
4
|
public static void main(String[] args) throws Exception { StudentManager.getInstance().addStudent( "Betty" , 17 , "55555555" ); } |
運行就不運行了,反正最后結果是"添加學生信息成功",數據庫里面多了一條數據。注意一下:
1、增刪改用的是executeUpdate()方法,因為增刪改認為都是對數據庫的更新
2、查詢用的是executeQuery()方法,看名字就知道了"Query",查詢嘛
可能有人注意到一個問題,就是Java代碼在insert后并沒有對事物進行commit,數據就添加進數據庫了,也能查出來,這是為什么呢?因為JDK的Connection設置了事物的自動提交。如果在addStudent(...)方法里面這么寫:
1
2
|
Connection connection = DBConnection.mysqlConnection; connection.setAutoCommit( false ); |
autoCommit這個屬性原來是true,JDK自然會幫助開發者自動提交事物了。OK,如果要改成手動提交事物的代碼,那么應該這么寫addStudent(...)方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public void addStudent(String studentName, int studentAge, String studentPhone) throws Exception { Connection connection = DBConnection.mysqlConnection; connection.setAutoCommit( false ); PreparedStatement ps = connection.prepareStatement( "insert into student values(null,?,?,?)" ); ps.setString( 1 , studentName); ps.setInt( 2 , studentAge); ps.setString( 3 , studentPhone); try { ps.executeUpdate(); connection.commit(); } catch (Exception e) { e.printStackTrace(); connection.rollback(); } } |
要記得拋異常的時候利用rollback()方法回滾掉事物。
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持服務器之家!
原文鏈接:http://www.cnblogs.com/xrq730/p/4851648.html