基於Java對象的數據庫增刪改查操作

目標:

    讓完全不懂數據庫,不懂SQL的程序員,能輕鬆地進行數據庫記錄的增刪改查操作。減少重複的DAO操作編碼,提高開發效率。


分析:

一、表記錄類型:

    一般應用系統的表記錄類型可以分爲以下三類:

1 配置記錄:記錄着系統的一些配置參數。一般一個表只有一條記錄,只需要實現讀和寫操作即可

2 增量記錄:記錄着實體的信息。一般都有一個ID作爲主鍵,來唯一標識這條記錄。需要提供增、刪、改、查操作。

3 日誌記錄:記錄着系統的運行狀態,接口交換狀態,操作記錄等。一般都不需要索引,不需要唯一標識。需要提供寫和查詢操作即可。

4 關聯記錄:用於關聯表與表之間多對多的關係。需要提供讀取和設置操作。

二、表字段類型:

    表的字段類型一般可以分爲:布爾、數值、字符串、時間,TEXT和BLOB類型一般不用於檢索。各類型的查詢條件:

1 布爾:=

2 數值:=,>=,>,<,<=,<>

3 時間:同上。(一般是針對時間段的查詢)

4 字符串:=,LIKE


設計:

一、PO對象的約束:

    要求每個PO對象的成員數據和表的字段名相同,這樣方便反射方式把數據字段和對象成員數據做對應。成員數據都必須是:

1 布爾:Boolean

2 數值:Byte(1),Short(2),Integer(4),Long(8),Float(4),Double(8)

3 時間:java.util.Date

4 字符串:String

5 BLOB:byte[]

下面分別對不同記錄類型進行設計:

1 配置記錄:不用做任何約束

2 增量記錄:第一個數據爲Integer id,作爲記錄的主鍵

3 日誌記錄:第一個數據爲Date logtime,作爲日誌記錄時間

4 關聯記錄:無需做PO

二、PO對象的操作:

    PO對象一般涉及到增加、刪除、修改、查詢操作。增刪改操作的參數相對比較簡單,基本由一個PO對象作爲參數即可。查詢操作由於查詢條件多種多樣,需要傳遞比較多的參數。

    查詢:傳遞3個參數,第一個是查詢描述參數,第二個是查詢數據1,第三個是查詢數據2。如Select(PO desc, PO data1, PO data2)。如果desc.field=null,該字段將不作爲查詢條件。各種字段類型的描述:

1 布爾:desc.field=true, 取data1.field

2 數值:=(1),<>(2),>=(3),<=(4),>= AND <=(5)。desc.field=前面的值,前面4個,只需取data1.field;第5個取data1.field 和 data2.field

3 時間:同上。desc.field= new Date(1)

4 字符串:desc.field="=" OR "LIKE"。取data1.field

  查詢的排序:排序分順序(ASC)和倒序(DESC),支持多個字段的複合排序。

三、PO對象的關聯

/**
 * 多對多關聯表,在刪除時需要刪除關聯表的相關記錄
 */
public class RelTable
{
 /**
  * 關聯表表名
  */
 String tablename;
 /**
  * 關聯表對於本表的外鍵名
  */
 String myfkeyname;
 /**
  * 關聯表對於對方表的外鍵名
  */
 String peerfkeyname;
}

四、接口設計:

1 配置記錄的讀與寫:

/**
 * 配置記錄的DAO模板接口
 */
public interface CfgRecDao<PO>
{
 /**
  * 讀記錄
  */  
 public PO Read() throws Exception;
 /**
  * 寫記錄
  */  
 public void Write(PO data) throws Exception;
}

2 增量記錄的增刪改查:

/**
 * 增量記錄DAO模板接口
 */
public interface IncRecDao<PO>
{
 //下面是PO desc的成員常量
 public static final Boolean BOOL_EQU= true; //=

 public static final String STRING_EQU="="; //=
 public static final String STRING_LIK="~"; //LIKE

 public static final Date DATE_BIGEQU= new Date(1);   //>=
 public static final Date DATE_SMAEQU= new Date(2);   //<=
 public static final Date DATE_BIGEQUandSMAEQU= new Date(3); //>= AND <=

 public static final byte NUMERIC_EQU=1;     //=
 public static final byte NUMERIC_NOTEQU=2;    //<>
 public static final byte NUMERIC_BIGEQU=3;    //>=
 public static final byte NUMERIC_SMAEQU=4;    //<=
 public static final byte NUMERIC_BIGEQUandSMAEQU=5;  //>= AND <=

public static final Boolean ORDER_BOOL_ASC= true;
public static final Boolean ORDER_BOOL_DESC= false;
public static final String ORDER_STRING_ASC="ASC";
public static final String ORDER_STRING_DESC="DESC";
public static final Date ORDER_DATE_ASC= new Date(1);
public static final Date ORDER_DATE_DESC= new Date(2);
public static final byte ORDER_NUMERIC_ASC=1;
public static final byte ORDER_NUMERIC_DESC=2;


 /**
  * 插入一條記錄
  * @param data: 數據對象,插入成功後得到主鍵值(id)
  * @return: 無
  * @throws Exception
  */
 public void Insert(PO data) throws Exception;
 /**
  * 刪除一條記錄,同時刪除多對多關聯表裏面的相關記錄
  * @param data: 數據對象,只需要有主鍵值(id)
  * @return: 無
  * @throws Exception
  */
 public void DeleteByID(int id) throws Exception;
 /**
  * 刪除一批記錄
  * @param arg: 查詢條件,若爲null,不刪除
  * @return: 無
  * @throws Exception
  */
 public void Delete(PO arg) throws Exception;
 /**
  * 更新一條記錄
  * @param data: 數據對象,需要有主鍵值(id)
  * @return: 無
  * @throws Exception
  */
 public void Update(PO data) throws Exception;
 /**
  * 唯一查詢
  * @param arg: 查詢條件,若爲null,則不限條件
  * @return: 返回一個符合條件的數據對象
  * @throws Exception
  */
 public PO SelectOne(PO arg) throws Exception;
 /**
  * 主鍵查詢
  * @param id: 主鍵查詢
  * @return: 返回一個符合條件的數據對象
  * @throws Exception
  */
 public PO SelectByID(int id) throws Exception;

  /**
 * 設置查詢的排序方式
 * @param desc: 爲null的字段不做排序
 * @throws Exception
 */
public void SetOderBy(PO desc) throws Exception;
/**
 * 設置查詢的唯一方式
 * @param desc: 爲null的字段不做唯一限制
 * @throws Exception
 */
public void SetDistince(PO desc) throws Exception;
/**
  * 全匹配複合查詢
  * @param arg: 查詢條件,若爲null,則不限條件
  * @return: 返回符合條件記錄數
  * @throws Exception
  */
 public int SelectCount(PO arg) throws Exception;
 /**
  * 全匹配複合查詢
  * @param arg: 查詢條件,若爲null,則不限條件
  * @param offset: 在記錄集的第幾條開始返回,>=0
  * @param count: 分頁顯示條數,若爲0或負數,則返回所有記錄
  * @return: 返回一組符合條件的數據對象
  * @throws Exception
  */
 public PO[] Select(PO arg,int offset,int count) throws Exception;
 /**
  * 複合查詢
  * @param desc: 查詢條件描述
  * @param data1: 查詢條件數據1
  * @param data2: 查詢條件數據2
  * @return: 返回符合條件記錄數
  * @throws Exception
  */
 public int SelectCount(PO desc,PO data1,PO data2) throws Exception;
 /**
  * 複合查詢
  * @param desc: 查詢條件描述
  * @param data1: 查詢條件數據1
  * @param data2: 查詢條件數據2
  * @param offset: 在記錄集的第幾條開始返回,>=0
  * @param count: 分頁顯示條數,若爲0或負數,則返回所有記錄
  * @return: 返回一組符合條件的數據對象
  * @throws Exception
  */
 public PO[] Select(PO desc,PO data1,PO data2,int offset,int count) throws Exception;

  /**
  * SQL直接查詢
  * @param sql: SELECT COUNT(*) 開頭
  * @return: 返回符合條件記錄數
  * @throws Exception
  */
 public int SelectCountBySql(String sql) throws Exception;
 /**
  * SQL直接查詢
  * @param sql: SELECT * 開頭
  * @return: 返回一組符合條件的數據對象
  * @throws Exception
  */
 public PO[] SelectBySql(String sql) throws Exception;

///////////////////////// 關聯表記錄 /////////////////////////
 /**
  * 設置多對多關聯表記錄,在實體記錄刪除時,關聯表記錄自動刪除
  * @param thisid: id
  * @param reltab: 關聯表名及對方id
  * @param fkeyids: 對方id
  * @throws Exception
  */
 public void SetRelationIds(int thisid,String reltab,int[] fkeyids) throws Exception;
 /**
  * 獲取多對多關聯id
  * @param thisid: id
  * @param reltab: 關聯表名及對方id
  * @throws Exception
  */
 public int[] GetRelationIds(int thisid,String reltab) throws Exception;
 /**
  * 搜索多對多關關係的對象
  * @param thisid: id
  * @param reltab: 關聯表名
  * @param objtb: 目標表名
  * @param objcls: 目標對象類型
  * @return: 返回符合條件的數據對象,用Y[]類型轉換
  * @throws Exception
  */
 public Object SelectRelationObjs(int thisid,String reltab,String objtb,Class<?> objcls) throws Exception;
}

3 日誌記錄的增加和查詢:

/**
 * 日誌記錄DAO模板
 */
public interface LogRecDao<PO>
{
 /**
  *寫入日誌記錄
  * @param data: 日誌
  */
 public void Insert(PO data) throws Exception;
/**
 * 設置查詢的排序方式
 * @param desc: 爲null的字段不做排序
 * @throws Exception
 */
public void SetOderBy(PO desc) throws Exception;
/**
 * 設置查詢的唯一方式
 * @param desc: 爲null的字段不做唯一限制
 * @throws Exception
 */
public void SetDistince(PO desc) throws Exception;
 /**
  * 複合查詢
  * @param desc: 查詢條件描述
  * @param data1: 查詢條件數據1
  * @param data2: 查詢條件數據2
  * @return: 返回符合條件記錄數
  * @throws Exception
  */
 public int SelectCount(PO desc,PO data1,PO data2) throws Exception;
 /**
  * 複合查詢
  * @param desc: 查詢條件描述
  * @param data1: 查詢條件數據1
  * @param data2: 查詢條件數據2
  * @param offset: 在記錄集的第幾條開始返回,>=0
  * @param count: 分頁顯示條數,若爲0或負數,則返回所有記錄
  * @return: 返回一組符合條件的數據對象
  * @throws Exception
  */
 public PO[] Select(PO desc,PO data1,PO data2,int offset,int count) throws Exception;
}
4 工廠方法:

/**
 * DAO模板工廠
 */
@SuppressWarnings("unchecked")
public class DaoFactory
{
 /**
  * 配置數據庫連接參數
  * @param dbcfg:數據庫連接參數
  * @throws Exception
  */
 public static void ConfigDBC(DBconfig dbcfg) throws Exception
 {
  ConnectionPool.init(dbcfg);
 }

 /**
  * 配置記錄的DAO創建
  * @param pocls: PO對象類型
  * @param tbName: 數據庫表名
  * @return: 配置記錄的DAO對象
  * @throws Exception
  */
 public static CfgRecDao<?> CreateCfgRecDao(Class<?> pocls,String tbName) throws Exception
 {
  return new CfgRecDaoImpl(pocls, tbName);
 }
 /**
  * 增量記錄的DAO創建
  * @param pocls: PO對象類型
  * @param tbName: 數據庫表名
  * @param pkname: 表的主鍵名
  * @param reltables: 關聯表名及鍵名
  * @return: 增量記錄的DAO對象
  * @throws Exception
  */
 public static IncRecDao<?> CreateIncRecDao(Class<?> pocls,String tbName,String pkname,RelTable[] reltables) throws Exception
 {
  if(pkname==null)
   throw new Exception("Need primary key for table");
  return new IncRecDaoImpl(pocls, tbName, pkname, reltables);
 }
 /**
  * 日誌記錄的DAO創建
  * @param pocls: PO對象類型
  * @param tbName: 數據庫表名
  * @return: 日誌記錄的DAO對象
  * @throws Exception
  */
 public static LogRecDao<?> CreateLogRecDao(Class<?> pocls, String tbName) throws Exception
 {
  return new IncRecDaoImpl(pocls, tbName, null, null);
 }
}

例程:

以用戶,用戶組,文件資源爲例,爲了給用戶授權可以訪問哪些文件資源,需要把用戶賦予某個組,而對文件資源的訪問權限,也由組去設置。這樣,用戶和組是多對多的關係,用戶組和文件資源也是多對多的關係。PO類定義:

public class UserPO
{
 public Integer id;
 public String name;
 public String account;
 public String pwdsha1hex;
}

public class UserGroup {
 public Integer id;
 public String name;
}
public class FileResource {
 public Integer id;
 public String name;
 public Date modifytime;
}
操作測試:
//插入、修改和刪除測試
static void TestInsertUpdateAndDelete() throws Exception
{
 IncRecDao<UserGroup> ugdao= TestCreateDAO();
 UserGroup data= new UserGroup();
 data.name= "insert測試";
 ugdao.Insert(data);
 System.out.println("Insert OK");
 data.name= "update測試";
 ugdao.Update(data);
 ugdao.DeleteByID(data.id);
 System.out.println("Delete OK");
}
//查詢測試
static void TestSelect() throws Exception
{
 IncRecDao<UserGroup> ugdao= TestCreateDAO();
 UserGroup orderby= new UserGroup();
orderby.name= IncRecDao.ORDER_STRING_ASC;
 ugdao. SetOderBy(orderby); //設置查詢排序
UserGroup data1= new UserGroup();
 data1.id=1;
 int count= ugdao.SelectCount(data1);
 UserGroup[] datas= ugdao.Select(data1, 0, 0);
 System.out.println("Select count: "+count+"\r\n"+datas);
 UserGroup desc= new UserGroup();
 desc.id=IncRecDao.NUMERIC_BIGEQUandSMAEQU;
 desc.name=IncRecDao.STRING_EQU;
 data1.name="test";
 UserGroup data2= new UserGroup();
 data2.id=6;
 count= ugdao.SelectCount(desc, data1, data2);
 datas= ugdao.Select(desc, data1, data2, 0, 0);
 System.out.println("Select count: "+count+"\r\n"+datas);
}


總結

本設計能滿足基本的增刪改查操作,對於一般的業務系統,基本夠用。


一、長處:

1對比起其他O/R工具,比如Hibernate,不用做複雜的O/R配置,開發效率高。
2 直接以對象爲參數進行查詢,不用寫查詢語句,降低了對Java程序員數據庫知識水平的要求。
3 通過模板類,對數據類型做了簡單的檢查,減少出錯



二、不足之處:

1 不支持複雜的查詢條件
2 因爲運用了很多反射機制,性能上有所降低
3 對於複雜的業務邏輯,這裏抽象出來的基本操作恐怕不夠用,需要再擴展












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章