Spring將替我們完成所有使用JDBC API進行開發的單調乏味的、底層細節處理工作。
操作JDBC時Spring可以幫我們做這些事情:
定義數據庫連接參數,打開數據庫連接,處理異常,關閉數據庫連接
我們僅需要關注:
聲明SQL語句,處理每一次得到的結果
一個較為簡單的例子與講解
JdbcTemplate類
JdbcTemplate是core包的核心類。它替我們完成了資源的創建以及釋放工作,從而簡化了我們對JDBC的使用。它還可以幫助我們避免一些常見的錯誤,比如忘記關閉數據庫連接。JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創建、執行,而把SQL語句的生成以及查詢結果的提取工作留給我們的應用代碼。它可以完成SQL查詢、更新以及調用存儲過程,可以對ResultSet進行遍歷並加以提取。它還可以捕獲JDBC異常並將其轉換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。
代碼分為三部分
第一部是利用Spring處理業務,第二部分創建Spring相關的XML,第三部分將XML和Context結合到一起。
package com.jiaozg.dao; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.springframework.jdbc.core.JdbcTemplate; import com.jiaozg.model.Dept; import com.jiaozg.util.SpringUtil; 主要類: public class DeptDao { private JdbcTemplate jdbcT; public void setJdbcT(JdbcTemplate jdbcT) { this.jdbcT = jdbcT; } public List findALL() { String sql = "select * from dept"; return jdbcT.queryForList(sql); } public List<Dept> findALLDepts() { List<Dept> depts = new ArrayList<Dept>();; String sql = "select * from Dept"; List list = jdbcT.queryForList(sql); Iterator iterator = list.iterator(); Dept dept = null; while (iterator.hasNext()) { Map map4dept = (Map) iterator.next(); dept = new Dept(); dept.setDeptNo(((BigDecimal) map4dept.get("DEPTNO")).intValue()); dept.setDName((String)map4dept.get("DNAME")); dept.setLoc((String)map4dept.get("LOC")); depts.add(dept); } return depts; } public int delete(int bid){ String sql = "delete from DeptInfo where bid =?"; return jdbcT.update(sql, new Object[]{bid}); } public static void main(String[] args) { DeptDao dao = (DeptDao) SpringUtil.getBean("deptDao"); List<Dept> depts = dao.findALLDepts();; for(Dept dept:depts){ System.out.println(dept.getDeptNo()+","+dept.getDName()+","+dept.getLoc()); } System.out.println("---------------------------------"); List list = dao.findALL(); for(Iterator it = list.iterator(); it.hasNext(); ) { System.out.println(it.next()); } } }
public class SpringUtil { private static ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); public static Object getBean(String beanName){ return ctx.getBean(beanName); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="springDSN" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"> </property> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"> </property> <property name="username" value="scott"></property> <property name="password" value="Qwer1234"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false" lazy-init="false" autowire="default" dependency-check="default"> <property name="dataSource"> <ref bean="springDSN" /> </property> </bean> <bean id="deptDao" class="com.jiaozg.dao.DeptDao"> <property name="jdbcT"> <ref bean="jdbcTemplate" /> </property> </bean> </beans>
總結:
JAVA邏輯部分還能看懂,但XML就有些迷糊:XML第一部分是配置到數據庫的連接信息,第二三部分就不知道了。
還有DAO是什么意思?
應該還缺少數據庫中表的定義,這個定義應該對應着DEBT的javabean.
一個更加詳細的例子
CREATE TABLE Student( ID INT NOT NULL AUTO_INCREMENT, NAME VARCHAR(20) NOT NULL, AGE INT NOT NULL, PRIMARY KEY (ID) );
Student表,有ID, NAME, AGE三項
對應的JAVABEAN
public class Student { private Integer age; private String name; private Integer id; public void setAge(Integer age) { this.age = age; } public Integer getAge() { return age; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setId(Integer id) { this.id = id; } public Integer getId() { return id; } }
StudentMapper.java的內容
public class StudentMapper implements RowMapper<Student> { public Student mapRow(ResultSet rs, int rowNum) throws SQLException { Student student = new Student(); student.setId(rs.getInt("id")); student.setName(rs.getString("name")); student.setAge(rs.getInt("age")); return student; } }
將搜索結果ResultSet轉化為Object
StudentJDBCTemplate.java
public class StudentJDBCTemplate implements StudentDAO { private DataSource dataSource; private JdbcTemplate jdbcTemplateObject; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; this.jdbcTemplateObject = new JdbcTemplate(dataSource); } public void create(String name, Integer age) { String SQL = "insert into Student (name, age) values (?, ?)"; jdbcTemplateObject.update( SQL, name, age); System.out.println("Created Record Name = " + name + " Age = " + age); return; } public Student getStudent(Integer id) { String SQL = "select * from Student where id = ?"; Student student = jdbcTemplateObject.queryForObject(SQL, new Object[]{id}, new StudentMapper()); return student; } public List<Student> listStudents() { String SQL = "select * from Student"; List <Student> students = jdbcTemplateObject.query(SQL, new StudentMapper()); return students; } public void delete(Integer id){ String SQL = "delete from Student where id = ?"; jdbcTemplateObject.update(SQL, id); System.out.println("Deleted Record with ID = " + id ); return; } public void update(Integer id, Integer age){ String SQL = "update Student set age = ? where id = ?"; jdbcTemplateObject.update(SQL, age, id); System.out.println("Updated Record with ID = " + id ); return; } }
Spring和JAVABEAN的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd "> <!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- Definition for studentJDBCTemplate bean --> <bean id="studentJDBCTemplate" class="com.yiibai.StudentJDBCTemplate"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
main.java
public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); StudentJDBCTemplate studentJDBCTemplate = (StudentJDBCTemplate)context.getBean("studentJDBCTemplate"); System.out.println("------Records Creation--------" ); studentJDBCTemplate.create("Zara", 11); studentJDBCTemplate.create("Nuha", 2); studentJDBCTemplate.create("Ayan", 15); System.out.println("------Listing Multiple Records--------" ); List<Student> students = studentJDBCTemplate.listStudents(); for (Student record : students) { System.out.print("ID : " + record.getId() ); System.out.print(", Name : " + record.getName() ); System.out.println(", Age : " + record.getAge()); } System.out.println("----Updating Record with ID = 2 -----" ); studentJDBCTemplate.update(2, 20); System.out.println("----Listing Record with ID = 2 -----" ); Student student = studentJDBCTemplate.getStudent(2); System.out.print("ID : " + student.getId() ); System.out.print(", Name : " + student.getName() ); System.out.println(", Age : " + student.getAge()); } }
還有一個帶有用法的main.java,我就不貼了
總結
1. 將JAVA的類與數據庫中的表項綁定到一起,這樣更加方便操作
2. 表到類的轉換過程需要rawMapper,它起作用體現在query或queryForObject
3. SQL語句用到了(name, age) (?, ?)的東西,這個倒是有點像C++11中的bind用法,QueryForObject也用到了類似的寫法,但是稍微復雜點
queryForObject(SQL, new Object[]{id}, new StudentMapper()}; 這里的id被填充到一個數組中,數組為SQL提供數據
4. Context讀取XML文件,Template依靠Context獲得Bean,這個bean不是數據的定義,而是操作的定義。
一些細節:
1. ResultSet就是執行SQL語句返回的結果集
2. ResultSetMetaData 用於返回記錄集的自身信息
3. DataSource 這個的介紹有點復雜,總體來看還是為了安全性與封裝
連接數據庫通常需要實現以下幾個步驟:
1. 注冊數據庫驅動程序(driver)。可以通過調用java.sql.DriverManager類的registerDriver方法顯式注冊驅動程序,也可以通過加載數據庫驅動程序類隱式注冊驅動程序。
2. 建立連接。調用java.sql.DriverManager類的getConnection()方法可以建立與數據庫的連接。
從實際應用的角度出發,我們可以看出采取這種方式連接到數據庫存在幾個問題。
第一是安全性問題,由於程序代碼中包含用戶名和密碼,其他人如果能得到bytecode,可以通過反編譯工具獲得用戶名和密碼。第二是代碼的可移植性問題。如果希望連接的數據庫名稱或用戶名有所更改,程序員需要修改源程序,然后把修改過的程序發送給用戶。也就是說,軟件無法脫離數據庫獨立存在。這樣不僅會大大提高軟件的成本,也不利於軟件本身的發展。還可能出現這樣的情況:在某些情況下,提供數據的機構不希望數據庫的用戶名和密碼讓編寫程序的程序員知道知道。
這樣就提出了一個問題,如何使Java和數據庫之間建立連接時隱藏一些敏感的信息。
數據源(Data Source)及JNDI數據源是在JDBC 2.0中引入的一個概念。
在JDBC 2.0擴展包中定義了javax.sql.DataSource接口來描述這個概念。
如果用戶希望建立一個數據庫連接,通過查詢在JNDI服務中的數據源,可以從數據源中獲取相應的數據庫連接。
這樣用戶就只需要提供一個邏輯名稱(Logic Name),而不是數據庫登錄的具體細節。
在這里有必要簡單介紹一下JNDI。JNDI的全稱是Java Naming and Directory Interface, 可以理解為Java名稱和目錄服務接口。JNDI向應用程序提供了一個查詢和使用遠程服務的機制。這些服務可以是任何企業服務。對於JDBC應用程序來說,JNDI提供的是數據庫連接服務。當然JNDI也可以向數據庫提供其他服務,但是這超出了本文范圍,在此不做論述。其實JNDI並不難理解。簡單來說,名稱服務提供了一個把文件,打印機,服務器等實體映射到一個邏輯名稱的機制。例如在操作系統中的名稱服務就把打印機映射到一個I/O端口。而目錄服務可以理解為名稱服務的一個擴展,它允許在服務中的各項擁有自己的屬性。又以打印機為例,打印機可以是彩色打印機,支持雙面打印,支持網絡打印,支持高速打印等。所有這些打印機的屬性都可以儲存在目錄服務中,和相應的打印機聯系起來。一些常見的目錄服務有NIS,NIS+,LDAP和Novell的NDS等。JNDI使應用程序通過使用邏輯名稱獲取對象和對象提供的服務,從而使程序員可以避免使用與提供對象的機構有關聯的代碼。例如在下面的例子中使用了在JNDI中的數據源,程序員就不需要提供Oracle8i驅動程序的名稱,這樣代碼的移植能力就更強。
下面詳細介紹一下數據源和javax.sql.DataSource接口。
在數據源中存儲了所有建立數據庫連接的信息。就象通過指定文件名你可以在文件系統中找到文件一樣,通過提供正確的數據源名稱,你可以找到相應的數據庫連接。javax.sql.DataSource接口定義了如何實現數據源。
在該接口中定義了九個屬性。
dataSourceName String 數據源接口實現類的名稱。
description String 對數據源的描述。
networkProtocol String 和服務器通訊使用的網絡協議名。
password String 用戶登錄密碼。
portNumber Int 數據庫服務器使用的端口,缺省值為1521。
serverName String 數據庫服務器名稱。
user String 用戶登錄名。
在javax.sql.DataSource接口中定義了很多方法通過這些方法,程序員可以獲得建立連接需要的所有信息。
需要注意的是,程序員不可以獲取登陸密碼,這就在一定程度上保證了安全性。需要注意的另一點是所有的方法都是synchronized方法,這是為了保證應用程序的線程安全(Thread-safe)。
如果在調用該方法時,即使數據源實例發生變化不會影響程序的正確運行。
然后通過該數據源對象進行數據庫操作。在這個例子中,程序和名稱服務環境都是在同一台計算機上運行。
在實際的應用中,程序可以通過RMI或CORBA向名稱服務環境注冊或查詢對象。例如在一個服務器-客戶機結構中,客戶機上的應用程序只需要知道數據源對象在服務器名稱服務環境中的邏輯名稱,就可以通過RMI向服務器查詢數據源,然后通過建立與數據庫的連接.這樣就可以解決本文最開始提出的問題。