移动数据库和J2ME工具探讨

来源:互联网 发布:万网域名淘宝帐号 编辑:程序博客网 时间:2024/06/05 19:44
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 160x600, 创建于 08-4-23MSDN */google_ad_slot = "4367022601";google_ad_width = 160;google_ad_height = 600;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>

  摘要

  在不可靠的无线网络中应用程序可靠性已经成为关系移动商业成功的一个中心话题。ME/' target='_blank' />J2ME(Java 2 平台微型版)是支持离线模式智能移动客户端的完美平台。整合了移动关系数据库的企业J2ME客户端能够管理在设备上的复杂数据并能在需要时和后台服务器同步。在这篇文章中,作者首先讨论了高可靠性移动移用应用程序的“偶然连接”(occasionally-connected)范例。他在这个范例中解释了移动数据库的作用。他还用一个J2ME应用程序阐述了移动数据库应用程序的关键组件。最后,他评论了主流的J2ME数据库公司,为你在购买所需要的数据库时提供一个指导。

  目前的无线网络是极不可靠的。不可靠的应用程序毁坏了移动商业的核心价值,而它又许诺无论何时何地都可以访问到信息。实际上,总是保持连接的需求正是基于无线应用协议(WAP)的瘦客户移动商业失败的主要原因。移动商业需要智能的移用客户端,它能够操纵当地的应用程序并且甚至在网络出现临时的不可靠时可以持续的工作(例如,离线操作)。“偶然连接”移动应用程序需要高级的移动数据储存和管理工具。这样的工具在J2ME上是容易得到的。

  在这篇文章中,我首先讨论了移动数据库的一般概念和规范。我用了一个应用程序例子来阐述J2ME移动数据库应用程序的重要设计和关键组件。我也对现在变化的市场如何正确选择正确的移动数据库提供了建议。

  设备上的数据存储和管理

  对离线操作的首要需求是存储和管理设备自身的应用数据。标准的J2ME仅仅提供有限的数据存储和管理能力。在低端的MIDP(移动信息设备描述)电话上,我们仅靠记录管理系统record manageMEnt system(RMS)得到线性的记录储存;在基础描述里(或PersonalJava)我们有简单的随机文件。应用程序开发者们必须用那些一般工具来组织数据,这些工具会对大应用程序导致乏味、低效率和错误的倾向。

  在J2SE(Java2平台标准版)和J2EE(Java2平台企业版)的世界中,关系数据库代表着数据管理的关键技术。大多数开发人员已经非常熟悉通过如JDBC(Java数据库连接)的API访问普通数据库。因此轻量级关系数据库是我们对移动数据管理自然的选择。

  除了提供离线操作模式外,设备上数据库on-device databases还有其他的重要好处:

  1、他们能管理用户参数。极度的个性化已经作为移动商业的一个主要优点被吹捧。

  2、他们能提供一个性能高速缓存器performance cache来减少网络往返回调。这样就能大大的增强在目前低速和反应慢的无线网络中应用程序的性能。

  JDBC可选包

  标准JDBC API对高端Java移动设备已经是可以选择利用了。PersonalJava运行时常常和可选的JDBC1.x API捆绑在一起。PersonalJava的继承物,J2ME 基础/个性化的描述Foundation/Personal Profile(FP/PP),它没有委托JDBC来访问数据库。相反,FP/PP设备通过J2ME JDBC可选包(JDBC OP)使用JDBC 3.x API 的一个富子集。JSR Java Specification Request(Java 规范需求)169正在发展这个可选包的规范,它的草案已在2002年11月提交上去。JDBC OP除了以下几个特点外支持大多数用于JDBC的公共特性:

  1、连接池

  2、ParaMEterMEtaData接口

  3、在CallableStateMEnt interface里为已经存储的程序通过名称来设置参数

  4、SQL 99的类型(如Struct,Array,Ref,SQLData,SQLInput,和SQLOutput接口 )

  5、自定义类型映射(如setTypeMap() 和getTypeMap() 方法)

  数据同步

  偶然连接应用程序范例的另一个重要特性是数据同步。独立的移动数据库被数据包所隔开。当我们用强大的后台服务器连接那些“孤岛”时移动应用程序非常有用。已连接上的移动数据库有如下优点:

  1、移动客户端常常需要访问企业的基础设施。例如,移动销售应用程序需要更新库存数据。

  2、后端应用程序需要上卷数据和聚集数据来制定智能商业决策或生成正确的报表。例如,供应链应用程序在决定它应该从供应商那里定购多少货物之前需要得到移动销售人员的聚集数据。

  3、后台能共享每个移动终端信息

  因此智能客户端不但应该提供离线设备数据管理,而且应该当网络可用时提供后台数据库的数据同步。后台数据库当作简单移动设备管理的一个内容储存库是同步解决方案的额外收益。

  理论上应用程序开发者能操纵整个同步逻辑,然而开发一个优化的,安全的和可扩展的同步解决方案需要非常专业的技能。移动数据库产品同其附带的同步工具和API一起赶走了开发人员的痛苦。

  什么是SyncML?

  SyncML是一个流行的应用层同步协议。除了包括像ME/' target='_blank' />J2ME这样的通用客户应用平台,移动设备通常还预装一些智能应用程序。例如在智能电话或PDA上包括有日历,地址本和联系表(个人信息管理 personal information manageMEnt, 或PIM应用程序)这些应用程序通常能够通过设备软件供应商所提供的协议与桌面PC或其他中央数据储存库进行数据同步。协议间的不兼容造成用户的许多问题。例如,在Pocket PC上的Mac 和Linux两平台进行同步是非常困难的;如果一个人拥有多个移动设备,那他必须安装多个驱动程序,而这些驱动程序是存在潜在冲突的。用户需要一个标准的同步协议,它允许任何设备在不需要专门驱动程序条件下与任何后端应用程序进行同步。

  SyncML是一个标准的XML数据格式,它定义语法描述简单PIM数据例如vCard和vCalendar。SyncML能访问数据库驱动的企业后端信息系统。例如,IBM WebSphere Everyplace Access提供SyncML客户端访问Lotus Notes和Microsoft Exchange servers。像IBM DB2和Oracle9i Database这样重要的企业数据库已经建立了对SyncML的支持。由于SyncML只是在PIM类型应用程序上发挥作用,它还没有强大到足以和通用关系数据库同步的地步,因此本文没有集中在SyncML上。

  一个应用程序例子

  现在通过一个简单的例子,我们检测一下移动数据库应用程序的典型用法和关键组件。

  移动联系管理器

  这是一个由PointBase提供的移动联系管理器的例子。联系管理器 contact manager包括在PointBase 4.x中。为了读者方便,我已经把源代码打包成zip文件放在Resource中。如果你想编译和运行例子,你必须先从PointBase处下载适当的jar文件。

  这个应用程序本身比较简单。它主要沿用了高级地址本应用程序的通用特性。例如,它允许用户存储联系人名字,地址和电话号码;提供自觉浏览和搜索接口;和后台数据库服务器同步。图1和图2分别显示了该应用程序在标准模式和同步模式下的操作。这些屏幕快照来自一个由Insignia’s Jeode PersonalJava VM驱动的Pocket PC 和一个由J2SE驱动的Mac OS X 膝上型电脑。相同字节代码的应用程序没有经过修改运行在许多平台上,证明了Java的威力。

 

  图1 在袖珍PC Jeode PersonalJava上的标准联系管理器

 

  图2 在Mac OS X上的两个同步的联系管理器spoke

  客户端应用程序UI(用户界面)是用AWT写的。这是被PersonalJava或J2ME/FP/PP设备所支持的唯一标准UI库。除了这些UI驱动,我们还有另一个代码层,它提供访问一般的设备上JDBC数据库。这个数据库访问层也提供了与后台服务器同步移动数据的逻辑,它是通过PointBase专有UniSync同步服务器来实现的。现在我们来看看数据访问层的代码,它包括在一个单独的类:DBManager.

  设备上的数据访问

  类DBManager是一个单独的类,它提供从应用程序单点访问数据。这个单独模式避免了嵌入式数据库的线程复杂性。下面的代码片断显示了DBManager的构造器和初始化的代码。它连接数据库,定义表,将测试数据导入表中,创建为以后时候的SQL状态模版(PreparedStateMEnt)。正如我们所看到的,这里用到的都是标准JDBC。对于企业Java 开发者下面的代码应该很容易明白:

  例1 连接移动数据库和初始化访问对象

 

  class DBManager {

  // DBManager is a singleton class.

  private static DBManager instance;

  private String driver;

  private String url;

  private String user;

  private String password;

  private boolean delay;

  private Connection connection;

  private StateMEnt stateMEnt;

  private PreparedStateMEnt insert;

  private PreparedStateMEnt find;

  private PreparedStateMEnt delete;

  private PreparedStateMEnt update;

  private PreparedStateMEnt all;

  static DBManager getInstance() {

  if (instance == null) {

  instance = new DBManager();

  }

  return instance;

  }

  private DBManager() {

  // Get paraMEters from runtiME properties.

  // This allows us to switch to different JDBC databases

  // without changing the application code.

  Properties properties = ContactManager.getProperties();

  driver =

  properties.getProperty("driver", "com.pointbase.ME.jdbc.jdbcDriver");

  url =

  properties.getProperty("url", "jdbc:pointbase:micro:pbdemo");

  user =

  properties.getProperty("user", "PBPUBLIC");

  password =

  properties.getProperty("password", "PBPUBLIC");

  delay =

  properties.getProperty("delayread","true").equals("true");

  connect();

  }

  private void connect() {

  try {

  // Load the driver class.

  Class.forNaME(driver);

  // If the database doesn't exist, create a new database.

  connection = DriverManager.getConnection(url, user, password);

  // Create template stateMEnt objects.

  stateMEnt = connection.createStateMEnt();

  createStateMEnt();

  // If the database is newly created, load the schema.

  boolean newdb=initDatabase();

  // Load sample data for the new tables.

  if(newdb) {

  SampleDataCreator.insert(connection);

  }

  } catch (Exception e) {

  e.printStackTrace();

  System.exit(1);

  }

  }

  void disconnect() {

  try {

  connection.commit();

  stateMEnt.close();

  insert.close();

  find.close();

  delete.close();

  update.close();

  all.close();

  connection.close();

  System.exit(0);

  } catch (Exception e) {

  e.printStackTrace();

  System.exit(1);

  }

  }

  // Create the table and load the schema.

  private boolean initDatabase() {

  try {

  String sql = "CREATE TABLE NaMECard (ID INT PRIMARY KEY, "+

  "NaME VARCHAR(254), Company VARCHAR(254), Title VARCHAR(254), "+

  "Address1 VARCHAR(254), Address2 VARCHAR(254), "+

  "Phone VARCHAR(254), Email VARCHAR(254), "+

  "Picture Binary(1000000))";

  // If the table already exists, this will throw an exception.

  stateMEnt.executeUpdate(sql);

  // This MEans the database already exists.

  return true;

  } catch (SQLException e) {

  // Ignore the error - the table already exists, which is good

  // so we don't need to add demo data later on.

  return false;

  }

  }

  // Create stateMEnt templates.

  private void createStateMEnt() {

  try {

  insert = connection.prepareStateMEnt(

  "INSERT INTO NaMECard (ID, NaME, Company, Title, Address1, "+

  "Address2, Phone, Email, Picture) "+

  "valueS (?, ?, ?, ?, ?, ?, ?, ?, ?)");

  find = connection.prepareStateMEnt(

  "SELECT * FROM NaMECard WHERE (NaME LIKE ?) "+

  "AND (Company LIKE ?) AND (Title LIKE ?) "+

  "AND ((Address1 LIKE ?) OR (Address2 LIKE ?)) "+

  "AND (Phone LIKE ?) AND (Email LIKE ?)");

  delete = connection.prepareStateMEnt(

  "DELETE FROM NaMECard WHERE ID = ?");

  update = connection.prepareStateMEnt(

  "UPDATE NaMECard SET ID=?, NaME=?, Company=?, Title=?, "+

  "Address1=?, Address2=?, Phone=?, Email=?, Picture=? "+

  "WHERE ID = ?");

  all = connection.prepareStateMEnt(

  "SELECT ID, NaME, Company, Title, Address1, Address2, "+

  "Phone, Email FROM NaMECard");

  } catch (SQLException e) {

  e.printStackTrace();

  }

  }

  // Other MEthods.

  }

  在DBManager中的其他方法通过简单JDBC API调用进行访问数据库。如下的代码片断展示了搜索和操纵名称卡片记录的方法。这些方法使用了我们之前定义的SQL模版。

  例2 数据访问方法

 

  Vector findNaMECardsByKeyword(String naME, String company,

  String title, String address1, String address2,

  String phone, String email) {

  Vector NaMECards = new Vector();

  String[] keywords = {naME, company, title, address1, address2,

  phone, email};

  try {

  for (int i = 0; i < keywords.length; i++) {

  String criteria = (keywords[i].equals("")) ? "%" :

  "%" + keywords[i] + "%";

  find.setString(i + 1, criteria);

  }

  ResultSet resultSet = find.executeQuery();

  while (resultSet.next()) {

  NaMECard naMECard = new NaMECard(resultSet.getInt(1),

  resultSet.getString(2), resultSet.getString(3),

  resultSet.getString(4), resultSet.getString(5),

  resultSet.getString(6),

  resultSet.getString(7), resultSet.getString(8));

  if (!delay)

  loadPicture(naMECard);

  NaMECards.addEleMEnt(naMECard);

  }

  } catch (SQLException e) {

  e.printStackTrace();

  }

  return NaMECards;

  }

  void addNaMECard(NaMECard naMECard) {

  naMECard.setID(getNewID());

  try {

  insert.setInt(1, naMECard.getID());

  insert.setString(2, naMECard.getNaME());

  insert.setString(3, naMECard.getCompany());

  insert.setString(4, naMECard.getTitle());

  insert.setString(5, naMECard.getAddress1());

  insert.setString(6, naMECard.getAddress2());

  insert.setString(7, naMECard.getPhone());

  insert.setString(8, naMECard.getEmail());

  insert.setBytes(9, naMECard.getPicture().getBytes());

  insert.executeUpdate();

  } catch (SQLException e) {

  e.printStackTrace();

  }

  }

  void updateNaMECard(NaMECard naMECard) {

  try {

  update.setInt(1, naMECard.getID());

  update.setString(2, naMECard.getNaME());

  update.setString(3, naMECard.getCompany());

  update.setString(4, naMECard.getTitle());

  update.setString(5, naMECard.getAddress1());

  update.setString(6, naMECard.getAddress2());

  update.setString(7, naMECard.getPhone());

  update.setString(8, naMECard.getEmail());

  update.setBytes(9, naMECard.getPicture().getBytes());

  update.setInt(10, naMECard.getID());

  update.executeUpdate();

  } catch (SQLException e) {

  e.printStackTrace();

  }

  }

  void deleteNaMECard(NaMECard naMECard) {

  try {

  delete.setInt(1, naMECard.getID());

  delete.executeUpdate();

  } catch (SQLException e) {

  e.printStackTrace();

  }

  }

  void loadPicture(NaMECard naMECard) {

  try {

  ResultSet resultSet =

  stateMEnt.executeQuery(

  "SELECT Picture FROM NaMECard WHERE ID = " +

  naMECard.getID());

  resultSet.next();

  Picture picture = new Picture();

  picture.setBytes(resultSet.getBytes(1));

  naMECard.setPicture(picture);

  } catch (SQLException e) {

  e.printStackTrace();

  }

  }

  private int getNewID() {

  try {

  ResultSet resultSet = stateMEnt.executeQuery(

  "SELECT MAX(ID)+1 FROM NaMECard");

  if (resultSet.next()) {

  return resultSet.getInt(1);

  } else {

  return 0;

  }

  } catch (Exception e) {

  e.printStackTrace();

  }

  return 0;

  }

  与后台数据库同步

  类DBManager也允许应用程序开发者用PointBase 专有UniSync引擎与后台数据库同步移动数据。不同的厂商使用不同的同步引擎,但他们的概念都是相类似的。同步过程按照如下这些步骤进行:

  1. 在后台服务器和移动设备上创建相应的数据库和表

  2. 在同步服务器上创建一个hub。这个hub包含发布信息。它指定和标识用于同步(发布)的后台表(或部分表)。

  3. 使用hub来创建spoke。spoke是在同步服务器上表示移动设备的对象。每个spoke都有一个ID。它能通过在同一个hub里的订阅对象来订阅发布。通过使用一个spokeID,移动设备匹配spoke并对订阅的后台表进行同步。

  4. 启动同步服务器。基本上通过com.pointbase.ME.sync.Server 类的main()方法来执行。这个服务器类用于PointBase 发布包。还有其他几个方法在不同环境中运行服务器。您可以参考PointBase文档来得到更多的细节和例子。默认情况下,服务器监听端口8124。

  5. 使用一个spokeID和在移动设备上的类 spoke stub 来初始化同步过程。

 

  图3 UniSync同步服务器框架图解

  例3 中的类ResetServer显示了在UniSync服务器上如何创建hub和spoke:

  例3 安装同步服务器

 

  manager=SyncManager.getInstance(caturl,catdriver,catuser,catpassword);

  String dsnaME;

  dsnaME=SyncDataSource.DEFAULT;

  String hubnaME="Hub";

  Hub hub=manager.createHub(hubnaME);

  Publication pub;

  String pubnaME;

  SpokeConfig spoke;

  Subscription sub;

  String subnaME="SubNaMECard";

  String tablenaME="NAMECARD";

  String[] tables=new String[]{tablenaME};

  // Publish the complete naME-card table

  pubnaME="PubNaMECard";

  pub=hub.newPublication(pubnaME,dsnaME,tables);

  hub.publish(pub);

  // Create two spokes and subscribe to this publication

  for(int i=1;i<=2;i++) {

  String naME="Spoke"+i;

  spoke=hub.createSpokeConfig(naME);

  spoke.savePassword("pass"+i);

  sub=spoke.newSubscription(subnaME,SyncDataSource.DEFAULT,pubnaME);

  spoke.subscribe(sub);

  }

  // Publish the naME-card table without the picture column

  pubnaME="PubNaMECardNoPicture";

  pub=hub.newPublication(pubnaME,dsnaME,tables);

  SyncTable table=pub.getSyncTable(tablenaME);

  table.dropSyncColumns(new String[]{"PICTURE"});

  hub.publish(pub);

  // Create two spokes and subscribe to this publication

  for(int i=3;i<=4;i++) {

  String naME="Spoke"+i;

  spoke=hub.createSpokeConfig(naME);

  spoke.savePassword("pass"+i);

  sub=spoke.newSubscription(subnaME,SyncDataSource.DEFAULT,pubnaME);

  spoke.subscribe(sub);

  }

  manager.close();

  下面的DBManager代码片断显示了如何获得spoke stub 和如何在设备上处理同步。代码中的注释解释了应用程序的同步和独立版本的不同:

  例4 通过同步服务器访问数据

 

  // Import proprietary classes for sync

  import com.pointbase.ME.jdbc.*;

  class DBManager {

  // In addition to JDBC connection variables,

  // we also need to define variables for sync

  // ... ...

  private Spoke spoke;

  private String spokenaME;

  private int spoke_id;

  private int spoke_range_start,spoke_range_end;

  final static int ROWS_PER_SPOKE=1<<16;

  private String syncurl;

  private String syncpassword;

  private DBManager() {

  // Get DB connection paraMEters

  // ... ...

  // Get sync paraMEters

  syncurl =

  properties.getProperty("syncurl", "http://localhost:8124";);

  String spokeid =

  properties.getProperty("spokeid", "1");

  spokenaME =

  properties.getProperty("spoke", "Spoke"+spokeid);

  syncpassword =

  properties.getProperty("syncpassword", "pass"+spokeid);

  url =

  properties.getProperty("url",

  "jdbc:pointbase:micro:pbdemo"+spokeid);

  connect();

  }

  // The complete connect MEthod using synchronization server

  private void connect() {

  try {

  System.out.println("Connecting to the database...");

  Class.forNaME(driver);

  // If the database doesn't exist, create a new database

  connection = DriverManager.getConnection(url, user, password);

  stateMEnt = connection.createStateMEnt();

  // Check sync MEtadata and create tables

  loadMEta();

  // Create prepared stateMEnts

  createStateMEnt();

  } catch (Exception e) {

  e.printStackTrace();

  System.exit(1);

  }

  }

  // The complete newID MEthod using the sync server

  private int getNewID() {

  try {

  ResultSet rs = stateMEnt.executeQuery(

  "SELECT MAX(ID)+1 FROM NaMECard WHERE "+

  "ID>="+spoke_range_start+" AND ID<"+spoke_range_end);

  rs.next();

  int id=rs.getInt(1);

  if(rs.wasNull()) {

  return spoke_range_start;

  } else {

  return id;

  }

  } catch (Exception e) {

  e.printStackTrace();

  }

  return 0;

  }

  // Create table and load MEtadata from the sync hub

  void loadMEta() {

  try {

  SyncManager manager=SyncManager.getInstance(connection);

  spoke=manager.getSpoke(spokenaME);

  if(spoke==null) {

  System.out.println(

  "Loading MEtaData from url "+syncurl+

  " for spoke "+spokenaME+

  " using password "+syncpassword);

  spoke=manager.createSpoke(spokenaME);

  spoke.savePassword(syncpassword);

  spoke.saveHubURL(syncurl);

  spoke.loadConfig();

  spoke.getSnapshot();

  }

  spoke_id = spoke.getSpokeId();

  System.out.println("SpokeID is "+spoke_id);

  spoke_range_start = ROWS_PER_SPOKE * spoke_id;

  spoke_range_end = spoke_range_start + ROWS_PER_SPOKE - 1;

  } catch (SyncException e) {

  e.printStackTrace();

  }

  }

  // Synchronize spoke databases (mobile databases) with the hub

  // and backend databases

  void sync() {

  try {

  spoke.sync();

  } catch (SyncException e) {

  e.printStackTrace();

  }

  }

  // Other data access MEthods are the saME as the non-synced version.

  }

  ME/' target='_blank' />J2ME移动数据库的选择

  当然PointBase并不是J2ME移动数据库产品的唯一选择。在本文的剩余部分我会比较和讨论几种主流的竞争者。在开始以前先让我来阐述一下这个产品的前景。

  高端设备上的JDBC数据库

  对于运行PersonalJava或Personal Profile的高端设备,数据库能够通过JDBC API被访问。JDBC数据库对于程序来说是最容易的数据库类型。所有的厂商已经至少有一个这种种类的产品。最大的挑战是如何平衡这些功能。我们想要最小而且最快的数据库。它能够支持应用程序所需功能的准确集合。

  MIDP设备上的轻量级数据

  CLDC(有限连接设备配置Connected Limited Device Configuration)/MIDP并不支持JDBC接口。在MIDP设备上支持复杂数据管理的两个通用方法是:

  1、执行非常轻量的数据库和RMS上的类似JDBC的访问API。这个方法很耗费资源,但根据开发者开发能力和性能支持证明是最好的。

  2、直接扩展RMS类并在扩展的类上执行简单的行序列化,数据访问,索引/搜索和同步方法。这个扩展类是RMS上的一个细小的层次。它能模拟关系表的行为。

  在产品评论的章节里我将讨论这两种方法。

  同步服务器

  众所周知,同步是移动数据库的一个关键性能。大多数数据库厂商有他们自己专有的同步服务器。这些服务器提供许多附加功能来优化在移动环境下的同步过程。同步服务器的重要性能包括如下几点:

  1、智能冲突解决方案

  2、带宽减少

  3、端到端编密码

  4、后台引擎性能调整

  5、异步和可扩展更新

  接下来的几章里我将检查和比较几种主流厂商的产品。

  HSQL 数据库引擎

  开放源码的数据库引擎―HSQL是基于Thomas Mueller的Hypersonic SQL项目。它完全是由Java是编写的同时也是一种广泛用于嵌入式的数据库。它被包含在许多J2EE应用服务器中。在移动设备上,HSQL运行在PersonalJava和FP/PP平台上。HSQL是完全免费的。你能自由地将它重新部署到你的应用程序中,这对移动应用程序来说是容易的。

  HSQL提供一个支持%95JDBC接口和所有JDBC数据类型的JDBC驱程。它支持事务,外键甚至Java存储过程。在HSQL中的表能寄存在内存中和保存在磁盘文件中。HSQL只有少于160-KB的存储器足迹footprint。它也为PersonalJava设备发布了一个数据库管理控制台(经过Sharp Zaurus上的测试)

  然而,HSQL缺少一些商业移动数据库上所需的高级性能和安全功能。更重要的是,HSQL不能提供任何同步解决方案。它也缺少一个为MIDP设备的解决方案。

  PointBase Micro

  PointBase是一个纯Java嵌入式数据库方面的主流厂商。PointBase Micro数据库能运行在FP/PP/PersonalJava和MIDP平台上。

  在FP/PP/PersonalJava平台上PointBase Micro 数据库仅用91 KB 存储器足迹就能支持大多数JDBC和SQL功能。它也支持例如数据库编密码这样的高级功能。它不支持存储过程和接口CallableStateMEnt。因此MIDP不支持JDBC,PointBase为MIDP提供它自己的轻量级类似JDBC的API。PointBase Micro 数据库的MIDP版本提供了一个用浏览器打开的MIDlet数据库控制台。

  PointBase Micro 数据库通过UniSync 同步服务器能容易地同服务器端PointBase嵌入式数据库和Oracle 数据库同步。

  Sybase iAnywhere 解决方案

  在膝上型电脑上的轻量级数据库ianywhere的SQL Anywhere Studio已经拥有巨大的市场份额。在普遍设备上的移动数据库市场它也有一个强大的份额。Anywhere SQL Studio 的自定义数据库生成器是它一个关键创新。它让用户而不是数据库厂商决定如何平衡数据库存储器足迹和它所支持的功能。

  在Anywhere SQL Studio中用户能指定将在应用程序中调用的SQL状态SQL stateMEnt。它根据用户对根本性质和事务功能的选择生成一个自定义数据库。所生成的数据库是一个纯Java类以及伴随的一系列用户应用程序能调用的API。使用iAnywhere MobiLink 同步服务器的数据同步API也能加入到自定义数据库中去。

  除了自定义数据库生成器,iAnywhere也提供大存储器足迹和移动数据库的一般用途。它包括支持Java储存过程 Java stored procedure在内的许多安全,优化和可用性方面的功能。这些数据库为了达到更好的性能天生就运行在流行的移动平台上。Sybase iAnywhere SQL Studio 现在仍然不能支持MIDP数据库的自动生成。

  IBM DB2 Everyplace

  IBM DB2 Everyplace是IBM在移动数据库的礼物。对于IBM的客户来说,DB2 Everyplace非常好地整合了其他IBM企业组件(例如,DB2 Universal Database 和WebSphere MQ Everyplace)和IBM开发工具(例如,IBM WebSphere Studio Device Developer)。DB2 Everyplace本身运行在包括Palm OS,Symbian OS,Pocket PC,QNX 软件系统和嵌入式Linux在内的许多平台上。DB2 Everyplace支持加密数据域和表存储优化功能。

  在MIDP平台上,DB2 Everyplace有一个叫FastRecordStore的产品。它仿效在MIDP RMS记录存储之上的可索引和可搜索的关系表。

  DB2 Everyplace 数据库和FastRecordStore通过IBM同步引擎与后台数据库同步。DB2 Everyplace也附带一个叫Mobile Application Builder 的工具,它允许开发者可视化地创建DB2 Everyplace 应用程序。

  Oracle9i Lite

  Oracle9i Lite是Oracle在移动数据库上的产品。它可运行在Plam OS,Pocket PC,Symbian OS和Win32 等平台上。Win32这个版本是为了能运行在膝上电脑上并支持JDBC,多用户模式以及Java存储过程。Oracle9i Lite的Pocket PC和Symbian OS版本支持JDBC。而Palm OS版本仅支持Oracle自身专用的OKAPI(对象核心API Object Kernel API)和ODBC(开放数据库连接Open Database Connectivitiy)。Oracle9i Lite suite包括一个移动开发工具Mobile DevelopMEnt Kit,它能够根据用户自定义需求自动生成和打包移动数据库应用程序。目前它只能生成本身客户端应用程序。

  Oracle9i 移动数据库通过Oracle移动服务器和后台Oracle数据库服务器同步。如果我们使用Oracle9i Lite 移动开发工具来创建应用程序则移动服务器会自动为这个应用程序生成同步逻辑。Oracle9i Lite支持任何基于TCP/IP的网络上的同步,包括HTTP,CDPD(单元数字包数据cellular digital packet data),802.11b无线局域网。通过使用移动服务器的开放传输API Open Transport APIs,我们也能增加新的传输。另外Oracle移动服务器支持非同时的同步。在高峰期每个设备只是把同步内容提交到一个队列中然后退出。异步操作是可扩展解决方案的关键。

  在MIDP平台上Oracle提供SODA(简单对象数据库访问Simple Object Database Access),这个产品是基于RMS的。SODA实际上是一个支持MIDP设备的面向对象数据库。它允许存储,搜索和恢复类似JavaBean的数据对象。

  选择正确的移动数据库

  选择正确的移动数据库产品是个复杂的商业决定。由于移动数据库嵌入在客户端应用程序中,你需要为你所开发的每个产品支付版税。为减少总开销而签订一个好的合同是非常重要的。由于同步解决方案所特有的性质每个产品都在某种程度上锁定了厂商。因此开发人员不仅应该考虑到产品的技术优点而且应该考虑到厂商的一贯声誉和稳定性。例如,如果你的移动客户端需要MIDP和Personal Profile 数据库,你应该选择同一厂商中更好传输率的产品和更易管理的同步解决方案。

  PointBase和Sybase iAnywhere提供极好的移动数据库解决方案。然而,如果你的企业基础架构主要是IBM或Oracle,那么最好选择DB2 Everyplace 或Oracle9i Lite 移动数据库

  移动数据库和同步解决方案是高可靠性移动应用程序的重要组件。他们提供了新的而且有前途的偶然连接移动应用程序范例。在阅读完本文后,你应该已经能够使用JDBC和PointBase APIs设计和执行移动数据库应用程序。由于移动数据库运行在小设备上,当选择商业产品时你需要仔细权衡特征和储存器足迹而不是性能。

  本文由yesky与Matrix-与Java共舞

<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 160x600, 创建于 08-4-23MSDN */google_ad_slot = "4367022601";google_ad_width = 160;google_ad_height = 600;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
原创粉丝点击