有的時候我們資料庫設計可能不會完全和程式碼語言一致,例如我們會在資料庫的每一張表前面加上一個特定的前綴用來區分,在BeetlSQL中將程式碼Pojo的名稱和資料庫Table名稱對應起來是使用NameConversion來轉換的,BeetlSQL內建了DefaultNameConversion、UnderlinedNameConversion和JPANameConversion等轉換器基本上可以滿足絕大部分的要求的,今天就來給大家示範一下如何自訂NameConversion。
前言
要了解本文的內容,首先你需要先了解Beetl和BeetlSQL這兩個項目,它們是來自於國人@閒·大賦嘔心瀝血精心打造的Java模板引擎和Java數據庫全功能Dao,而且目前官方已經提供了Beetl和BeetlSQL與市面上各種主流MVC框架的整合方案
我不敢說Beetl比JSP、FreeMarker、Velocity等大家熟知的一些模板引擎怎麼說的好,但是我想說以我個人的使用經驗來做對比,Beetl絕對不會比它們差,BeetlSQL更是不會比Hibernate和MyBatis等差
如果有興趣想了解一下Beetl和BeetlSQL,可以訪問Beetl官方論壇查閱詳情。
何不給Beetl一個機會來證明Beetl自己,更是給你自己一個學習工作嘗試的機會,說不定你就和我一樣一不留神就愛上了它。
正文
本文討論的是使用BeetlSQL自動產生Pojo物件時,透過自訂轉換器移除映射表名前綴的問題。
先看一組開發遇到的情況:
假定目前有個項目beetl在資料庫設計的時候,所有的表名都有一個項目前綴bt_,即所有的表都以bt_開頭,如bt_user、 bt_role、bt_user_role,看到這樣的表名設計我們可能第一想到的是使用UnderlinedNameConversion來進行轉化,於是乎:
bt_user-> BtUser
bt_role -> BtRole
肯定跟我一樣是不能接受這樣的命名的,所以我們需要自己定義一個NameConversion來進行轉換,去除掉Table生成Pojo時自動加上的Bt前綴NameConversion中核心的幾個方法getPropertyName、getColName、getTableName、 getClassName,根據方法名稱和參數很容易理解 因此我們只需要根據我們自己的實際需求重新實作一個NameConversion將表名轉類別名稱、類別名稱轉表名的方法重新實作以下即可。 具體做法:首先我們新建一個類別繼承自DefaultNameConversion類,分別重寫getClassName和getTableName兩個方法,最後在我們的專案中啟用自己寫的Conversion@Override@Overridepublic String getTableName(Class<?> c) { //遵循BeetlSQL规范,@Table拥有最高优先级 Table table = (Table)c.getAnnotation(Table.class); if(table!=null){ return table.name(); } //UserRole -> user_role String tableName = StringKit.enCodeUnderlined(c.getSimpleName()); //user_role -> bt_user_role return "bt_"+tableName; }@Overridepublic String getClassName(String tableName){ //假定所有表都是以bt_开头 //bt_user_role -> user_role tableName = tableName.substring(tableName.indexOf("_")+1); //user_role -> userRole String clsName = StringKit.deCodeUnderlined(tableName); //userRole -> UserRole return StringKit.toUpperCaseFirstOne(clsName); }
理想是丰满的,现实是骨感的。在QQ上与作者沟通遇到这种情况时的解决方案时发现可能会出现这样的情况: 前台用户使用的数据表可能是 usr_user,而后台管理员也属于用户呀,因此可能后台管理员的数据表为mgr_user,最后这样会导致两个类冲突了,生成代码的时候可能会被覆盖一个类,是有问题的。好在机智如我~遇到这样的根据功能模块给不同的表加上不同的前缀,我们在Java程序开发时也经常会使用不同的包名来区分不同的模块,我可以将不同前缀的实体放到对应的包下面,还原表名的时候读取一下包名即可反向解析出表名,下面是我的具体实现:
@Overridepublic String getClassName(String tableName){ //为了安全起见,做了个判断,理论上项目数据库设计好了应该是无需判断直接截取所有前缀 //usr_user_role -> user_role if(tableName.matches("^(usr_|base_|mgr_).*")){ tableName = tableName.substring(tableName.indexOf("_")+1); } //user_role -> UserRole String clsName = StringKit.deCodeUnderlined(tableName); return StringKit.toUpperCaseFirstOne(clsName); }@Overridepublic String getTableName(Class<?> c) { Table table = (Table)c.getAnnotation(Table.class); if(table!=null){ return table.name(); } //获取Package 最后一层 xxx.pojo.usr -> Usr String pkg = c.getPackage().getName(); pkg = pkg.substring(pkg.lastIndexOf(".")+1); pkg = Character.toUpperCase(pkg.charAt(0))+pkg.substring(1); //Usr+User -> UsrUser -> usr_user return StringKit.enCodeUnderlined(pkg+c.getSimpleName()); }
然后在使用BeetlSQL反向生成Pojo的时候,使用Filter将数据表分门别类的生成到不同的包下面
//记得初始化SQLManager时要使用自己写好的NameConvertionSQLManager sql = initSQLManager(); GenConfig config = new GenConfig(); sql.genALL("xxxx.pojo.usr", config, new GenFilter(){ @Override public boolean accept(String tableName) { return tableName.startsWith("usr_"); } }); sql.genALL("xxxx.pojo.mgr", config, new GenFilter(){ @Override public boolean accept(String tableName) { return tableName.startsWith("mgr_"); } }); sql.genALL("xxxx.pojo.base", config, new GenFilter(){ @Override public boolean accept(String tableName) { return tableName.startsWith("base_"); } });
最终会在项目中会生成以下Pojo
usr_user -> xxxx.pojo.usr.User.java
mgr_admin -> xxxx.pojo.mgr.Admin.java
base_city -> xxxx.pojo.base.City.java
即便是有类名冲突的,因为在不同的包下,所以也不会影响
就这样一次轻松而又愉快的BeetlSQL自定义去除Pojo和表前缀被解决啦~
最后
之前所有项目的开发都是Jsp,几乎没使用过其他第三方模板库,因为一次误打误撞让我认识了Beetl,它的轻巧,它独特的语法都深深的吸引了我,因为Beetl又让我认识了BeetlSQL,抱着试一试的心态尝试过之后总有一种把当前项目(使用的Hibernate)推倒重来的冲动,虽然最后因为项目的工程量和工期原因,当前的这个项目最终还是使用了Hibernate,但是我可以确定的是,在以后的开发道路上,如果允许,如果没有能让我更心动的通用Dao,BeetlSQL将会是我不二的选择。
今天在这里分享这篇自定义NameConvertion的功能,其实并没有多少技术含量,更多的是想做一次简单的推广,让更多人知道Beetl,让更多人爱上Beetl~