基于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 对于复杂的业务逻辑,这里抽象出来的基本操作恐怕不够用,需要再扩展












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