mongodb + helma 实现web快速开发

     Mongodb是一个高性能,开源,无模式的文档型数据库,在网上的介绍已经有很多,就不多说了。官方网址:http://www.mongodb.org/display/DOCS/Home。在某些对事务处理的要求不严格的项目中,可以考虑用它替代关系型数据库,好处是能够解决甚至是大型关系数据库都难以解决的性能问题。当然,也不是所有项目都适合。
      Helma是一个用来开发快速,稳定Web应用程序的开源框架。它使用JavaScript 来作为服务端脚本环境,从而可以除去编译周期。官方网址:http://helma.org/。
      那么这两者为什么要结合到一起呢?在学习Mongodb的实践中,我发现虽然现在的java框架有很多种,但是大都和关系型数据库联系紧密,针对nosql数据库的没有(很大可能是我没找到,毕竟java框架太多了)。Mongodb的特点是用二进制的JSON存储数据,在使用方式上有明显的脚本语言的特点,他的控制台管理工具,就是利用javascript语言进行管理。而Helma使用的也是javascript,这就是共同点。
      Helma以前我也曾经关注过,印象很深刻,他轻量,快速,稳定,独立性强。有着完美的拿来主义--可以及其简便的使用任何java类扩充功能,这就使他无所不能,这一点对我来说及其看重,我不想因为用上了某种框架,当用户要增加某项功能时,急得抓耳挠腮。当然,也有不好的,他内置一个XML数据库,通过配置文件可以实现ORM的映射。但是XML数据库的功能很弱,不堪大用。当然也可以使用任何一个支持jdbc连接的数据库,但是他的ORM映射又很简陋,远不如ruby on rails的灵活,处理多表关联很麻烦。你还可以直接使用SQL,抛弃ORM。但是经过jdbc封装的面向对象的方式和javascript写在一起总觉得别扭。
      现在,和mongodb做搭档情况就不同了,劣势转化成优势,java的强大,脚本语言的灵活可以完美结合,同时还不用担心部署的问题和运行效率,这是我迄今遇到的最好的一个解决方案。下面就以一个小例子进一步说明。这是一个简单的电话号码本的增删查改。

    首先是Global下的一个init.js,这里主要定义操作Mongodb的方法。

 

// mongodb functional
function getDb() {
  var conn = new com.mongodb.Mongo("localhost", 27017);
  var db = conn.getDB("test");
  return db;
}

function getCollection(table) {
  return getDb().getCollection(table);
}

function getDBObject() {
  return new com.mongodb.BasicDBObject();
}

function getDBObjectInit(key, val) {
  return new com.mongodb.BasicDBObject(key, val);
}

function getObjectId(id) {
  return new com.mongodb.ObjectId(id);
}

function jsonToDBObject(jsonData) {
  return com.mongodb.util.JSON.parse(jsonData);
}

function trim(source) {
  if (source == null) return '';
  return source.replace(/^\s+|\s+$/g, "")
}

 从这些函数,我们不难看出在Helma中使用java类是多么的简单,甚至比在java中使用他们还要简单。

 下面是first.hac,增删查改的逻辑都在其中。

 

var coll = getCollection('name_tel');

// add
var add_name = trim(req.data.add_name);
var add_tel = trim(req.data.add_tel);
if (add_name != '') {
  var addName = getDBObject();
  addName.put('name', add_name);
  addName.put('telephon', add_tel);
  coll.insert(addName);
}

// delete
if (req.data.id != null) {
  coll.remove(getDBObjectInit('_id', getObjectId(req.data.id)));
}

// update
if (req.data.upd_id != null) {
  var updName = coll.findOne(getDBObjectInit('_id', getObjectId(req.data.upd_id)));
  updName.put('telephon', trim(req.data.upd_tel))
  coll.save(updName);
}

// select
var curSelect = coll.find();
res.data.trRow = '';
while(curSelect.hasNext()){
  var cur = curSelect.next();
  res.data.trRow += '\
    <tr>\
      <td>' + cur.get('name') + '</td>\
      <td>' + cur.get('telephon') + '</td>\
      <td><a href="second?id=' + cur.get('_id') + '">修改</a></td>\
      <td><a href="first?id=' + cur.get('_id') + '">删除</a></td>\
    </tr>';
}
res.data.pushDate = new Date();
res.data.body = this.renderSkinAsString('first');
this.renderSkin('page');

  下面是page.skin,可以在其中设置共同的外观,菜单之类的东西。

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  <title>my test</title>
</head>
<body>
  <div>
    <img src="/static/helmaheader.png"/>
  </div>
  <% response.body %>
</body>
</html>

  下面是first.skin,画面中的主要内容

 

<script type="text/javascript">

  function trim(source) {
    return source.replace(/^\s+|\s+$/g, "")
  }
  
  function addItem() {
    var form = document.form1;
    if (trim(form.add_name.value) == '') {
      alert('姓名得有');
      return;
    }
    form.action = 'first';
    form.submit();
  }
  
</script>

<form name='form1' method="post">
  <p><% response.pushDate %></p>
  <table>
    <tr>
      <th>姓名</th>
      <th>电话</th>
      <th></th>
      <th></th>
    </tr>
    <% response.trRow %>
  </table>
  <hr/>
  <span>姓名:</span>
  <input type="text" name="add_name">
  <span>电话:</span>
  <input type="text" name="add_tel">
  <input type="button" value='添加' onClick="addItem();">
</form>

  下面是second.hac要跳转到的更新画面的逻辑

 

var coll = getCollection('name_tel');
var select_name = coll.findOne(getDBObjectInit('_id', getObjectId(req.data.id)));

res.data.pushId = select_name.get('_id');
res.data.pushName = select_name.get('name');
res.data.pushTel = select_name.get('telephon');
res.data.body = this.renderSkinAsString('second');
this.renderSkin('page');

 下面是second.skin,更新画面中的内容。

 

<script type="text/javascript">
  function updItem() {
    document.form1.action = 'first';
    document.form1.submit();
  }
</script>

<form name='form1' method="post">
  <p>
    <span>姓名:</span>
    <span><% response.pushName %></span>
  </p>
  <p>
    <span>电话:</span>
    <input type="text" name="upd_tel" value='<% response.pushTel %>'>
  </p>
  <input type="button" value="修改" onClick="updItem();">
  <a href="first">返回</a>
  <input type="hidden" name="upd_id" value='<% response.pushId %>'>
</form>

 好了,就这么多了,是不是很简单?在这个框架下开发,几乎和在ror下开发一样的舒服。还有一点是ror所没有的,就是Helma有一个很棒的调试环境,可以单步跟踪,察看变量内容,运行代码,虽然简单,但真的很方便。

    前面说了那么多优点,局限性也要说说。

首先是来自Mongodb的,他没有事务,操作也不是原子的,在某些十分重要的场合,例如金融行业,是不能用;Mongodb太过灵活,不像SQL,如果表名,字段名不对是会报错的,在测试中很容易就会发现,Mongodb的理念是不存在,我就给你建一个,所以要格外小心,别写错了。再说说Helma,最大的遗憾我觉得是skin里接收的只能是字符串类型的数据,有些复杂的显示逻辑不得不转移到hac中生成html片断返回,虽然不影响功能,但是毕竟显示层的东西写在两个地方看起来不太舒服。其他的,虽然没有用到ORM,但我觉得不是问题,只要我喜欢这种方式。老学究可能会不高兴,但是我觉得使用框架是要给我们带来方便,而不是束缚我们的枷锁。

你可能感兴趣的:(JavaScript,mongodb,Web,框架,orm)