目標:
讓完全不懂數據庫,不懂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 Integer id;
public String name;
}
public class FileResource {
public Integer id;
public String name;
public Date modifytime;
}
{
IncRecDao<UserGroup> ugdao= TestCreateDAO();
UserGroup data= new UserGroup();
data.name= "insert測試";
ugdao.Insert(data);
System.out.println("Insert OK");
ugdao.Update(data);
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);
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);
}
總結
本設計能滿足基本的增刪改查操作,對於一般的業務系統,基本夠用。