现在我们在大致分析db案例。在dbdemo包下是测试数据库的demo。我们看入口类Main代码:
public class Main extends TLBaseModule {
static long startTime;
private static boolean resultflag = false;
private static int i = 0;
public Main(String main, TLObjectFactory myfactory) {
super(main, myfactory);
}
public static void main(String[] args) {
TLMsg returnmsg = null;
String configdir = System.getProperty("user.dir") + "\\config\\person\\";
Myfactory myfactory = Myfactory.getInstance(configdir,null);
myfactory.boot();
Main m = new Main("main", (TLObjectFactory) myfactory);
m.registInfactory(myfactory);
TLMsg tmsg = new TLMsg().setAction("getTable")
.setParam("tableName", "user");
returnmsg = m.putMsg("database", tmsg);
TLTable tb = (TLTable) returnmsg.getParam(TLObjectFactory.FACTORY_MODULEINSTANCE);
// m.insert(tb);
m.query(tb);
// serverstart.i=0;
// m.testview(dataBase);
// m.batch(tb);
// m.find(tb);
// m.delete(tb);
// m.total(tb);
// m.findall(tb);
// m.userModle();
}
为节省篇幅,上面是入口Main函数。这个main函数与前面介绍的startserver的基本类似。先初始化模块工厂,工厂boot。这时候一个不同是,没有启动appCenter,而是直接获得了一个表模块,代码如下:
TLMsg tmsg = new TLMsg().setAction("getTable")
.setParam("tableName", "user");
returnmsg = m.putMsg("database", tmsg);
TLTable tb = (TLTable) returnmsg.getParam(TLObjectFactory.FACTORY_MODULEINSTANCE);
我们说过appCenter也是一个普通模块,启动它的目的是为了某些程序初始化。如果不需要初始化可以不用启动的。这里就是直接给database 发送一个消息 ,消息的内容是获得一个表名为user的表模块。我们给database发送消息,之前并没有创立database模块,这都是自动完成的。如果工厂发现database模块已经存在就直接使用,如果没有则创建,对模块使用者是透明的。获得表后,我们就可以对这表进行各种操作,下面就是表的各种操作测试:
// m.insert(tb);
m.query(tb);
// serverstart.i=0;
// m.testview(dataBase);
// m.batch(tb);
// m.find(tb);
// m.delete(tb);
// m.total(tb);
// m.findall(tb);
// m.userModle();
下面我们看其中的query(tb),这个函数是调用queryfunc来完成查询的:
private void queryfunc(TLTable tb, int number) {
startTime = System.currentTimeMillis();
String sql = "select * from table where name like ?";
LinkedHashMap<String, Object> sqlparams = new LinkedHashMap<>();
// sqlparams.put("number", number);
sqlparams.put("name", "%dong%");
TLMsg querymsg = new TLMsg().setAction("query")
.setParam("sql", sql)
.setParam("resultType", "arrayList")
.setParam("params", sqlparams)
.setParam("isWait", "false") //异步多线程
.setParam("resultFor", this)
.setParam("resultAction", "getResult");
// querymsg.setParam("cacheName", "table_user");
// querymsg.setParam("cacheKey", "" + number);
TLMsg returnMsg= putMsg(tb, querymsg);
}
我们的表TLTable是对apache的 dbutils封装,sql操作语法就是dbutils的语法。首先定义Sql语句,定义查询参数,然后创建查询消息querymsg,最后把查询消息发送给表:putMsg(tb, querymsg)。但这里没有返回结果处理,为什么呢?因为我们在查询消息中定义了异步查询:.setParam("isWait", "false") ,这告诉表,我的查询是异步的,我不等你结果了,你的结果返回给"resultFor" 定义的模块,这里设置的是自己this,返回的结果用"resultAction"定义的行为处理,这里定义的是"getResult",我们看看getResult方法:
private void getResult(Object fromWho, TLMsg msg) {
List datas = (List) msg.getParam("result");
if (datas.isEmpty()) {
System.out.println("getResult 返回没有数据");
return;
}
System.out.println("getResult 返回 数据----------------");
for (int i = 0; i < datas.size(); i++) {
Object[] unit = (Object[]) datas.get(i);
for (int j = 0; j < unit.length; j++) {
System.out.print(this.i + " " + unit[j] + " ");
this.i++;
}
System.out.println("");
}
Long nowTime = System.currentTimeMillis();
Long runtime = nowTime - startTime;
System.out.println("getResult 运行时间:" + runtime);
}
getResult方法接收到表返回来的结果,对结果的处理。我们看看运行结果:
我们看到getResult收到了结果并打印输出。这里发现getResult调用了两次,为什么呢?因为我们的查询语句 String sql = "select * from table where name like ?"; 其中的查询条件name 不是表的分表字段,所以查询的时候对所有表都查询了,看日志输出:
信息: 模块: userm 方法:query 内容:select * from userm where name like ? 进程id: Thread-2
八月 08, 2018 11:21:34 上午 cn.tianlong.tlobjcet.base.TLLog setLog0
信息: 模块: userw 方法:query 内容:select * from userw where name like ? 进程id: Thread-1
我们发出的是一个对user表的查询,由于user表分为两个表userm、userw,因此通过分表触发器发出两个对实际表的查询 。我们开始指定了查询为异步,因此两个查询在不同的线程里同时查询。查询结果都返回到getResult。因此我们看到getResult被调用了两次。这里user表分表字段是number ,number 小于100的在userw表,大于100的在userm表。如果直接通过number查询,则分表触发器可直接指定查询表了,不用所有的表都查询。我们看分表触发器UserTableSplitTrigger设计:
public class UserTableSplitTrigger extends TLDBBaseTriggerForSplitTable {
public UserTableSplitTrigger() {
super();
}
public UserTableSplitTrigger(String name ) {
super(name);
}
public UserTableSplitTrigger(String name , TLObjectFactory modulefactory){
super(name,modulefactory);
}
@Override
protected TLMsg selectTable(Object fromWho,TLMsg msg){
TLMsg nmsg=(TLMsg) msg.getParam(DOWITHMAG);
String isWait = (String) nmsg.getParam("isWait");
if(isWait!=null && isWait.equals("false"))
nmsg.setWaitFlag(false);
LinkedHashMap<String ,Object> tparams= (LinkedHashMap<String, Object>) nmsg.getParam("params");
if(tparams==null ||tparams.get("number")==null)
{
ArrayList<Object> totaldatas=new ArrayList<>();
TLMsg wreturn =changeTable("userw",nmsg);
TLMsg mreturn= changeTable("userm",nmsg);
List mdatas;
if(mreturn!=null && mreturn.getParam("result")!=null)
{
mdatas = (List) mreturn.getParam("result");
totaldatas.addAll(mdatas);
}
List wdatas ;
if(wreturn!=null && (List) wreturn.getParam("result")!=null)
{
wdatas = (List) wreturn.getParam("result");
totaldatas.addAll(wdatas);
}
return createMsg().setParam("result",totaldatas).setParam(DONEXTMSG,"false");
}
else
{
int numb= (int) tparams.get("number");
if(numb==0)
return null ;
if(numb >=100)
return changeTable("userm",nmsg);
else
return changeTable("userw",nmsg);
}
}
}
其中根据numb值改变表,如果sql中没有设置numb,则对所有表查询。这分表触发器是如何作用到user表上的呢,我们看数据库的配置database_config.xml中:
<tables>
<table name="user" dbtable="user" dbserver="dbserver1"
readserver="dbserver2" beforeTrigger="userTableSplitTrigger" />
<table name="userm" dbtable="userm" dbserver="dbserver1" beforeTrigger="cachetrigger"
afterTrigger="aftercachetrigger"/>
<table name="userw" dbtable="userw" dbserver="dbserver2" beforeTrigger="cachetrigger"
afterTrigger="aftercachetrigger" />
</tables>
对于表设置了前操作触发器 beforeTrigger="userTableSplitTrigger" ,在表操作前首先执行这个触发器, afterTrigger="aftercachetrigger" 表示表操作后执行该触发器。我们看配置文件里面的触发器配置目录:
<triggers>
<trigger name="userTableSplitTrigger" delete="onDelete" insert="onInsert" query="onQuery" update="onUpdate" />
<trigger name="cachetrigger" query="getCache" />
<trigger name="aftercachetrigger" query="writeCache" delete="deleteCache" update="deleteCache" />
</triggers>
定义了触发器在表哪个操作上执行。userTableSplitTrigger在所有操作上都执行。query="onQuery" 意思是当表执行query行为时,先执行触发器的onQuery行为。
对于触发器cachetrigger ,仅仅定义了当表执行query的时候 执行getCache行为。对于aftercachetrigger触发器,当表查询、更新后执行,目的是写或更新缓存。
这里看到对表user的分表、缓存都是通过配置设定的,没有更改表TLTable的代码,对TLTable表是透明的。如果取消分表或缓存,那么从配置文件中删掉配置就可以了。从这里看到程序是模块的自由组合,没有互相的依赖。
那么这些触发器是如何作用表上的呢,前面知道,我们通过数据库database来获得一个表的,那么看数据库是如何创建一个表的,在通用包db包下TLDataBase的模块:
private TLMsg getTable(Object fromWho, TLMsg msg) {
String tablename= (String) msg.getParam("tableName");
TLTable tableobj= (TLTable) dbObjs.get(tablename);
if(tableobj==null)
{
tableobj=createTable(tablename);
dbObjs.put(tablename,tableobj);
HashMap<String, String> tableparams=tables.get(tablename);
addTrigger( tableobj,tableparams);
}
return createMsg().setParam(INSTANCE,tableobj);
}
数据库首先看表模块存在不,不存在则创建一个新表模块,然后对这个表加上触发器 addTrigger操作,代码:
private void addTrigger(TLBaseModule tbobj, HashMap<String,String>viewparams){
if(viewparams==null)
return ;
for(String key:triggerName.keySet()){
String triggersStr=viewparams.get(key);
if(triggersStr!=null && !triggersStr.isEmpty())
{
String[] triggersNames=triggersStr.split(";");
HashMap<String ,String>tparams;
for(int n=0 ;n<triggersNames.length;n++)
{
tparams=triggers.get(triggersNames[n]);
addTriggerMstTable(tbobj,triggersNames[n],triggerName.get(key),tparams);
}
}
}
}
private void addTriggerMstTable(TLBaseModule tbobj,String triggerName,String type, HashMap<String ,String>tparams){
String[] actions={"delete","update","insert","query","batch"};
for(int i=0 ; i< actions.length ;i++){
if(tparams.get(actions[i])!=null)
{
insertTriggerMsgTable(tbobj,triggerName,type,actions[i],tparams.get(actions[i])) ;
}
}
}
private void insertTriggerMsgTable(TLBaseModule tbobj,String triggerName,String type,String onAction,String trigAction){
TLMsg trmsg=new TLMsg().setDestination(triggerName)
.setAction(trigAction);
TLMsg bmsg=createMsg().setAction(type).setParam("action",onAction)
.setParam("msg",trmsg);
putMsg(tbobj,bmsg);
}
最后在insertTriggerMsgTable里,给表发出加触发器的消息,这里面type定义了模块行为,我们看type是如何定义的,在database的初始化中init中:
@Override
protected void init() {
triggerName.put("beforeTrigger",ADDBEFOREMSG);
triggerName.put("afterTrigger",ADDAFTERMSG);
}
从这里就看到了,根据触发器的类型,给表模块发送不同的消息ADDBEFOREMSG或ADDAFTERMSG。表是一个标准TLBaseModule模块,我们看标准模块是如何处理这两个消息,在系统消息处理方法systemMsgs中:
case ADDBEFOREMSG:
if (beforeMsgTable == null)
beforeMsgTable = new HashMap<>();
addMsgTable(beforeMsgTable, "action", msg);
returnMsg=msg;
break;
case ADDAFTERMSG:
if (afterMsgTable == null)
afterMsgTable = new HashMap<>();
addMsgTable(afterMsgTable, "action", msg);
returnMsg=msg;
break;
从这里就看到了,前触发器就是模块的前期消息处理表,后期触发器就是消息后期处理表。
至此为止,通过表触发器设置这个案例,我们看到了通过标准模块的功能,可以灵活组件模块、更变程序行为。没有通过复杂java语句,比如反射、注解之类来实现。老实说,我对java语言也是刚接触不深,可以说边学边写,很多常用的函数也记不住,基本是百度,包括现在map遍历还每次百度,对于反射、注解也没有深入了解,所有代码中只有工厂创建模块的时候用了基本反射。我觉得注解的发明完全是只考虑了编程的方便,而忽视了对象的独立、完整性,内心就对它排斥导致看不下去。我我一看spring的那些注解就头疼,所以没兴趣学下去。任何一种框架、工具的目的都是为了解脱复杂工作、提高效率,如果一个工具需要一本厚厚的书才能懂得如何用,那么这个工具基本属于失败的。试想如果手机、汽车都配备一本厚厚的书让大家学习如何使用,那会怎样。我们的消息对象框架中,其实重点就一个类TLBaseModule,熟悉、使用非常容易。可以用在任何情况。如果你的对象不希望别人别对它动手动脚,那么继承它就行。然后告诉对方,只要告诉我干什么,我自己做,其他的你别管,当然他也管不了。写到这顺便唠叨下。