又到春节了,有挺长时间没有写博客了。
节前的最后两天上班,没啥事,玩玩我上回弄的那个Info-Persister的数据持久化代码生成机制吧。
标题很大,其实很简单的一件事情。
一直认为Hibernate和JDO之类的东西过于复杂且效率不高,虽然省却了写SQL的麻烦但是需要编写一个并不简单的配置文件,所以说这些东西也并不能说极大地方便了开发者。
而自己写DAL层代码最痛苦的事情莫过于重复,大量重复的代码,GET/SET让人抓狂。
在.NET的时候,我弄了一个自动生成DAL层的小工具,原理很简单:
1.约定表的名称和字段名称,如表名前加t_XXX,字段名前加f_XXX,工具根据这个生成VO(即Info)。
2.将数据库操作放入DB中,用存储过程代替程序代码中的SQL语句,并约定SP的名称,如p_XXX,工具根据这个生成PO(即Persister)。
我用的是约定而不是配置,因为我一直认为“约定强于配置”。
因此,这个工具相当简单了,读数据库,根据约定,生成VO和PO的代码(注意是源代码!),然后可以在自己的项目中加入生成的代码,一起编译就OK了。
我个人非常推崇这种作法,一是效率,所有的SQL都放在SP中。二是省事,不用再GET/SET敲大堆重复代码。
唯一的就是你需要自己写存储过程,不单是SQL了,还要写SP。
具体实现上就不多说,两个问题解决就一切OK了!
一是代码生成
这个容易,在.NET中有System.CodeDom可以方便地使用,java中没有了,就干脆直接拼字串得了。
二是获取数据表与存储过程的结构
下面主要来说一下这个问题,我折腾了三种常用库:SQL Server、MySQL和Oracle。
首先,根据表与字段的信息,生成VO的代码,我们需要知道有哪些表?每个表有哪些字段?每个字段又都是什么类型?
可以这样:
//MySQL //列出有哪些表 String sql = "show tables"; //每个表的字段信息 String sql = "describe "+tname; //SQLServer String sql = "select name from sysobjects where substring(name,1,2)='t_'"; String sql = "select t1.name,t3.name,t1.isnullable " +"from syscolumns t1 join systypes t3 on t1.xusertype=t3.xusertype " +"join sysobjects t4 on t1.id=t4.id where t4.name='"+tname+"'"; //Oracle String sql = "select table_name from user_tables where substr(table_name,1,2)='T_'"; String sql = "select column_name,data_type,nullable from user_tab_columns where table_name='"+tname+"'";
然后,根据SP的信息,生成PO代码,我们需要知道有哪些SP?每个SP的名称?参数?返回值?
这儿我将SP进行了分类,比如t_user表有p_user_list,p_user_delete,p_user_create...等多个SP。
可以这样:
//MySQL //列出包含哪些类的PO,比如User String sql = "select name from mysql.proc where name like 'p_%'"; //根据类找出它下面挂的SP,比如根据User查出它下挂的所有SP String sql = "select name from mysql.proc where substring(name,1,"+Integer.toString(len)+")='p_"+cname+"_'"; //根据SP找出它的参数列表 String sql = "select param_list from mysql.proc where name='"+pname+"'"; //SQLServer String sql = "select distinct substring(substring(name,3,len(name)-2),1,charindex('_',substring(name,3,len(name)-2))-1) " +" from sysobjects where substring(name,1,2)='p_'"; String sql = "select name from sysobjects where substring(name,1,"+Integer.toString(len)+")='p_"+cname+"_'"; String sql = "select t1.colid,t1.name,t2.name from syscolumns t1 join systypes t2 on t1.xusertype=t2.xusertype join sysobjects t3 on t3.id=t1.id " +"where t3.name='"+pname+"'"; //Oracle String sql="select distinct object_name from user_procedures where SUBSTR(object_name,1,2)='P_'"; String sql="select '"+cname+".'||procedure_name from user_procedures where object_name='"+cname+"'"; String sql="select position, argument_name, data_type,in_out from user_arguments where object_name='" +pnames[1]+"' and package_name='"+pnames[0]+"' order by position";
不过,Oracle比较麻烦的在于它的SP不能返回数据库,只能用OUT参数来处理了,但是它又有一个方便的PACKAGE,就不用在SP的名称上下功夫了,直接p_user.listAll就好了。
差不多就这样了,剩下的事情就只是一个工作量的问题了,花点时间弄一下就可以了。
最后,用一句话结束这个日记,放假了!