首先要明確任務:簡單的說就是將數據庫中的記錄封裝爲一個對象.
由上一層傳入一條查詢的SQL語句,和一個代表該對象的class對象來創建出一個該對象的實例返回給上一層.
爲了簡化,這條SQL語句明確指明瞭查找的記錄。比如:select * from user where id='1'
傳入的class對象可以由上一層使用Class.forName(Str)來得到。
查詢數據庫中的列名必須與對象的屬性名一致。(別名也可以)
一、總體思路:
1。首先要利用傳進來的class對象創建出該對象的實例。但還沒有對其屬性賦值。
2。然後要根據SQL語句去查詢數據庫,將所有結果都拿到。
3。既然前提是列名與該對象的屬性名相同,那麼列名對應的屬性肯定有一個設置該屬性的setXXX(obj)方法。
根據列名調用所有的set方法把查到的結果賦給對象的屬性。
4。將此對象返回給上一層。
二、問題與解決:
1。怎麼樣根據class對象獲得該類的實例:調用class的newInstance()方法,雖然返回的是一個object類型,但確實是傳入的class所代表的類的實例。
2。怎麼樣獲得連接:使用DBCP或者直接用DriverManager獲得都可以。
3。使用Statement 還是PrepareStatement:使用PrepareStatement。原因有3,略。
4。怎麼樣獲得列名和列數:使用PrepareStatement類的getMetaData()方法可以獲得表信息,然後再分別調用getColumnCount()獲得列數;getColumnLabel(i)獲得列的別名。
5。怎麼樣獲得結果集:PreparedStatement ps = conn.prepareStatement(sql) 先設置要執行SQL語句,用PrepareStatement對象來執行。
ResultSet rs = ps.executeQuery(); 就得到結果集。
6。怎麼樣調用setXXX(obj)方法:找到傳入的class的方法(也就是得到Method對象)後,調用Method對象的invoke("要調用該方法的實例對象", "該方法參數的對象")方法;
①。怎麼樣得到Method對象:要得到這個方法對象,首先得到該類的所有public方法的一個集合(clazz.getMethods()),然後,用"set"+列名進行字符串的拼接後,
與方法集合中每一個方法的名字進行忽略大小寫的比較,如果有找到了這個方法,就調用他,沒有找到,就不調用。
②。調用方法時,"要調用該方法的實例對象"是什麼:就是一開始用class的newInstance()方法創建出來的Object對象。表示是要調用這個對象的方法。
③。調用方法時,"該方法參數的對象"是什麼:因爲setXXX(obj)方法中的參數obj是要爲這個屬性設置的值,這個值也就是從數據庫中按照列名取出來的結果值.
獲得結果集後,由於不知道結果值是什麼類型,所以直接調用ResultSet對象的getObject("列名");得到該值所代表的對象,然後將他傳入invoke方法中。
(關於這個對象是什麼類型,可以去查文檔中的“MySQL Type And Java Type”或者直接用obj.getClass().getName()打出來看,但這裏我們沒必要知道它是什麼類型的。)
三、提醒與注意:
1。就算是已經知道結果集中只有一條記錄,還是要調用rs.next()才能讓結果集遊標指向該記錄,也就是才能調用獲得列值的那些方法,不然會有空指針異常。
2。調用setXXX(obj)方法 和 尋找class類中有沒有該set方法 應該被提取出來做成單獨的方法,不然會讓程序難以閱讀。
3。尋找class所代表的類有沒有相應的set方法時,要使用忽略大小寫(equalsIgnoreCase)的方式來比較。因爲一般set後面的屬性名首字母是大寫的,而數據庫中不區分大小寫。
4。我們剛開始的時候使用的是int id = getInt("列名"),來取得一個類型爲int的id對象。也可以使用Object id = getObject("列名"),來獲得,其實是同一個東西,用Object來獲得更靈活。
四、關於異常:
1。關於“可以自己處理的異常”和“不能自己處理的異常”:
① 如果一個異常在你這個程序裏,可以被你處理,那麼你可以catch住,並且做一些簡單處理;
② 如果這個異常是你自己無法處理,或者你無法判斷這個異常能被誰處理,那麼你可以把他拋爲一個運行時異常,並且在類中申明,這樣你的上一層如果能處理則處理,
不能處理可以不用管,異常將被帶到更上一層,當所有異常集中在頂層的時候,可以統一的來管理它們,一般是分別轉向不同的功能模塊。
2。關於運行時異常 : 上一層碰到運行時異常可以catch住處理也可以不catch,不處理就往上上層繼續拋了。
五、測試:
1。寫了一個User類, 擁有id,name,password,email屬性,相應的get/set方法,重寫toString()方法.
2。使用Class clazz = Class.forName("User");得到這個類的class對象。
3。數據庫中創建了相應的表和字段,SQL語句爲:select * from user where id='1'
4。將SQL語句和class對象傳入方法既可以得到該對象:User user = (User)createUser(SQL, clazz);
5。打印出該對象的toString方法:id : 1 name : yzj password : 123 email :
[email protected]
六、總結:
這樣就簡單的實現了O-R映射,如果要將對象中的屬性持久化到數據庫的表中,有一定的限制條件也可以用上面的思想來實現。
J2EE三層架構的思想是要讓每一層都相對的獨立,通過接口來互相調用。
老師的例子是加入了一個集合並查詢多條記錄,將每一條記錄轉化成一個對象放入集合後把集合返回給上一層。
代碼:
package cn.yzj;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import cn.yzj.user.User;
/*
* 題目:簡化版的O-R映射。
*/
public class Test4
{
/*
* main方法中模擬了上一層使用.
*/
public static void main(String[] args) throws Exception
{
Class clazz = Class.forName("cn.yzj.user.User");
User user = (User)new Test4().createUser("select * from user where id='1'", clazz);
System.out.println(user.toString());
}
/*
*主方法,創建對象.
*/
public Object createUser(String sql,Class clazz)
{
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Object obj;
try
{
obj = clazz.newInstance(); //得到要返回對象的實例
} catch (Exception e){
throw new RuntimeException(e.getMessage(),e);
}
try
{
conn = JyUtilThree.getConnection(); //獲得連接. 可以自行替換
ps = conn.prepareStatement(sql); //定義使用PreparedStatement來執行SQL.
rs = ps.executeQuery(); //執行查詢SQL.並返回結果集.
ResultSetMetaData rsmd = ps.getMetaData(); //獲得表的信息.
int col = rsmd.getColumnCount(); //獲得表中的列數.
while(rs.next())
{
for(int i = 0;i<col;i++)
{
String cloname = rsmd.getColumnLabel(i+1); //獲得表中列名
Object obj1 = rs.getObject(cloname); //獲得列的值.
invokeMeth(cloname,obj,obj1,clazz); //調用相應的set方法.
}
}
} catch (SQLException e){
e.printStackTrace();
throw new RuntimeException(e.getMessage(),e);
}
JyUtilThree.release(rs, ps, conn); //將資源釋放
return obj; //將對象返回.
}
/*
* 調用所有setXXX(obj)的方法
*/
public void invokeMeth(String cloname,Object instance,Object obj,Class clazz)
{
try
{
Method m = findMethod( "set"+cloname,clazz);
if(m!=null)
m.invoke(instance, obj);
} catch (Exception e)
{
e.printStackTrace();
throw new RuntimeException(e.getMessage(),e);
}
}
/*
* 查找是否存在該列名對應的setXXX(obj)的方法.
*/
public Method findMethod(String name ,Class clazz)
{
Method[] m= clazz.getMethods();
for(int i = 0;i<m.length;i++)
{
if(name.equalsIgnoreCase(m[i].getName()))
return m[i];
}
return null;
}
}