" >
站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2ME 专题高可靠性移动应用程序-移动数据库和J2ME工具(二) 内容: 翻译作者:jungleguo  2003-11-15
原文: 
http://www.javaworld.com/javaworld/jw-06-2003/jw-0606-wireless-p2.html
一个应用程序例子
现在通过一个简单的例子,我们检测一下移动数据库应用程序的典型用法和关键组件。
移动联系管理器
这是一个由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 连接移动数据库和初始化访问对象

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">class&nbsp;DBManager&nbsp;{
&nbsp;&nbsp;//&nbsp;DBManager&nbsp;is&nbsp;a&nbsp;singleton&nbsp;class.
&nbsp;&nbsp;private&nbsp;static&nbsp;DBManager&nbsp;instance;
&nbsp;&nbsp;private&nbsp;String&nbsp;driver;
&nbsp;&nbsp;private&nbsp;String&nbsp;url;
&nbsp;&nbsp;private&nbsp;String&nbsp;user;
&nbsp;&nbsp;private&nbsp;String&nbsp;password;
&nbsp;&nbsp;private&nbsp;boolean&nbsp;delay;
&nbsp;&nbsp;private&nbsp;Connection&nbsp;connection;
&nbsp;&nbsp;private&nbsp;Statement&nbsp;statement;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;insert;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;find;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;delete;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;update;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;all;

&nbsp;&nbsp;static&nbsp;DBManager&nbsp;getInstance()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(instance&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instance&nbsp;=&nbsp;new&nbsp;DBManager();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;instance;
&nbsp;&nbsp;}

&nbsp;&nbsp;private&nbsp;DBManager()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;parameters&nbsp;from&nbsp;runtime&nbsp;properties.
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;This&nbsp;allows&nbsp;us&nbsp;to&nbsp;switch&nbsp;to&nbsp;different&nbsp;JDBC&nbsp;databases
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;without&nbsp;changing&nbsp;the&nbsp;application&nbsp;code.
&nbsp;&nbsp;&nbsp;&nbsp;Properties&nbsp;properties&nbsp;=&nbsp;ContactManager.getProperties();
&nbsp;&nbsp;&nbsp;&nbsp;driver&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("driver",&nbsp;"com.pointbase.me.jdbc.jdbcDriver");
&nbsp;&nbsp;&nbsp;&nbsp;url&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("url",&nbsp;"jdbc:pointbase:micro:pbdemo");
&nbsp;&nbsp;&nbsp;&nbsp;user&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("user",&nbsp;"PBPUBLIC");
&nbsp;&nbsp;&nbsp;&nbsp;password&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("password",&nbsp;"PBPUBLIC");
&nbsp;&nbsp;&nbsp;&nbsp;delay&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("delayread","true").equals("true");
&nbsp;&nbsp;&nbsp;&nbsp;connect();
&nbsp;&nbsp;}

&nbsp;&nbsp;private&nbsp;void&nbsp;connect()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Load&nbsp;the&nbsp;driver&nbsp;class.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(driver);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;database&nbsp;doesn't&nbsp;exist,&nbsp;create&nbsp;a&nbsp;new&nbsp;database.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection&nbsp;=&nbsp;DriverManager.getConnection(url,&nbsp;user,&nbsp;password);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Create&nbsp;template&nbsp;statement&nbsp;objects.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement&nbsp;=&nbsp;connection.createStatement();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createStatement();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;database&nbsp;is&nbsp;newly&nbsp;created,&nbsp;load&nbsp;the&nbsp;schema.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;newdb=initDatabase();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Load&nbsp;sample&nbsp;data&nbsp;for&nbsp;the&nbsp;new&nbsp;tables.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(newdb)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SampleDataCreator.insert(connection);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;void&nbsp;disconnect()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection.commit();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(0);
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;Create&nbsp;the&nbsp;table&nbsp;and&nbsp;load&nbsp;the&nbsp;schema.
&nbsp;&nbsp;private&nbsp;boolean&nbsp;initDatabase()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sql&nbsp;=&nbsp;"CREATE&nbsp;TABLE&nbsp;NameCard&nbsp;(ID&nbsp;INT&nbsp;PRIMARY&nbsp;KEY,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Name&nbsp;VARCHAR(254),&nbsp;Company&nbsp;VARCHAR(254),&nbsp;Title&nbsp;VARCHAR(254),&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Address1&nbsp;VARCHAR(254),&nbsp;Address2&nbsp;VARCHAR(254),&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Phone&nbsp;VARCHAR(254),&nbsp;Email&nbsp;VARCHAR(254),&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Picture&nbsp;Binary(1000000))";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;table&nbsp;already&nbsp;exists,&nbsp;this&nbsp;will&nbsp;throw&nbsp;an&nbsp;exception.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement.executeUpdate(sql);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;This&nbsp;means&nbsp;the&nbsp;database&nbsp;already&nbsp;exists.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true;
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Ignore&nbsp;the&nbsp;error&nbsp;-&nbsp;the&nbsp;table&nbsp;already&nbsp;exists,&nbsp;which&nbsp;is&nbsp;good
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;so&nbsp;we&nbsp;don't&nbsp;need&nbsp;to&nbsp;add&nbsp;demo&nbsp;data&nbsp;later&nbsp;on.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;Create&nbsp;statement&nbsp;templates.
&nbsp;&nbsp;private&nbsp;void&nbsp;createStatement()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"INSERT&nbsp;INTO&nbsp;NameCard&nbsp;(ID,&nbsp;Name,&nbsp;Company,&nbsp;Title,&nbsp;Address1,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Address2,&nbsp;Phone,&nbsp;Email,&nbsp;Picture)&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"valueS&nbsp;(?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?)");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;*&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;(Name&nbsp;LIKE&nbsp;?)&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AND&nbsp;(Company&nbsp;LIKE&nbsp;?)&nbsp;AND&nbsp;(Title&nbsp;LIKE&nbsp;?)&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AND&nbsp;((Address1&nbsp;LIKE&nbsp;?)&nbsp;OR&nbsp;(Address2&nbsp;LIKE&nbsp;?))&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AND&nbsp;(Phone&nbsp;LIKE&nbsp;?)&nbsp;AND&nbsp;(Email&nbsp;LIKE&nbsp;?)");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"DELETE&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;ID&nbsp;=&nbsp;?");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"UPDATE&nbsp;NameCard&nbsp;SET&nbsp;ID=?,&nbsp;Name=?,&nbsp;Company=?,&nbsp;Title=?,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Address1=?,&nbsp;Address2=?,&nbsp;Phone=?,&nbsp;Email=?,&nbsp;Picture=?&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"WHERE&nbsp;ID&nbsp;=&nbsp;?");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;ID,&nbsp;Name,&nbsp;Company,&nbsp;Title,&nbsp;Address1,&nbsp;Address2,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Phone,&nbsp;Email&nbsp;FROM&nbsp;NameCard");
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Other&nbsp;methods.

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

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">Vector&nbsp;findNameCardsByKeyword(String&nbsp;name,&nbsp;String&nbsp;company,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;title,&nbsp;String&nbsp;address1,&nbsp;String&nbsp;address2,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;phone,&nbsp;String&nbsp;email)&nbsp;{
&nbsp;&nbsp;Vector&nbsp;NameCards&nbsp;=&nbsp;new&nbsp;Vector();
&nbsp;&nbsp;String[]&nbsp;keywords&nbsp;=&nbsp;{name,&nbsp;company,&nbsp;title,&nbsp;address1,&nbsp;address2,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone,&nbsp;email};
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;keywords.length;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;criteria&nbsp;=&nbsp;(keywords[i].equals(""))&nbsp;?&nbsp;"%"&nbsp;:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"%"&nbsp;+&nbsp;keywords[i]&nbsp;+&nbsp;"%";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find.setString(i&nbsp;+&nbsp;1,&nbsp;criteria);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;resultSet&nbsp;=&nbsp;find.executeQuery();
&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(resultSet.next())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NameCard&nbsp;nameCard&nbsp;=&nbsp;new&nbsp;NameCard(resultSet.getInt(1),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(2),&nbsp;resultSet.getString(3),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(4),&nbsp;resultSet.getString(5),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(6),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(7),&nbsp;resultSet.getString(8));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!delay)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadPicture(nameCard);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NameCards.addElement(nameCard);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
&nbsp;&nbsp;return&nbsp;NameCards;
}

void&nbsp;addNameCard(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;nameCard.setID(getNewID());
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;insert.setInt(1,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(2,&nbsp;nameCard.getName());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(3,&nbsp;nameCard.getCompany());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(4,&nbsp;nameCard.getTitle());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(5,&nbsp;nameCard.getAddress1());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(6,&nbsp;nameCard.getAddress2());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(7,&nbsp;nameCard.getPhone());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(8,&nbsp;nameCard.getEmail());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setBytes(9,&nbsp;nameCard.getPicture().getBytes());
&nbsp;&nbsp;&nbsp;&nbsp;insert.executeUpdate();
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

void&nbsp;updateNameCard(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;update.setInt(1,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(2,&nbsp;nameCard.getName());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(3,&nbsp;nameCard.getCompany());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(4,&nbsp;nameCard.getTitle());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(5,&nbsp;nameCard.getAddress1());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(6,&nbsp;nameCard.getAddress2());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(7,&nbsp;nameCard.getPhone());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(8,&nbsp;nameCard.getEmail());
&nbsp;&nbsp;&nbsp;&nbsp;update.setBytes(9,&nbsp;nameCard.getPicture().getBytes());
&nbsp;&nbsp;&nbsp;&nbsp;update.setInt(10,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;update.executeUpdate();
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

void&nbsp;deleteNameCard(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;delete.setInt(1,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;delete.executeUpdate();
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

void&nbsp;loadPicture(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;resultSet&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement.executeQuery(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;Picture&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;ID&nbsp;=&nbsp;"&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;resultSet.next();
&nbsp;&nbsp;&nbsp;&nbsp;Picture&nbsp;picture&nbsp;=&nbsp;new&nbsp;Picture();
&nbsp;&nbsp;&nbsp;&nbsp;picture.setBytes(resultSet.getBytes(1));
&nbsp;&nbsp;&nbsp;&nbsp;nameCard.setPicture(picture);
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

private&nbsp;int&nbsp;getNewID()&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;resultSet&nbsp;=&nbsp;statement.executeQuery(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;MAX(ID)+1&nbsp;FROM&nbsp;NameCard");
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(resultSet.next())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;resultSet.getInt(1);
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
&nbsp;&nbsp;return&nbsp;0;
}
</td></tr></table>与后台数据库同步
类DBManager也允许应用程序开发者用PointBase&nbsp;专有UniSync引擎与后台数据库同步移动数据。不同的厂商使用不同的同步引擎,但他们的概念都是相类似的。同步过程按照如下这些步骤进行:
1.&nbsp;在后台服务器和移动设备上创建相应的数据库和表
2.&nbsp;在同步服务器上创建一个hub。这个hub包含发布信息。它指定和标识用于同步(发布)的后台表(或部分表)。
3.&nbsp;使用hub来创建spoke。spoke是在同步服务器上表示移动设备的对象。每个spoke都有一个ID。它能通过在同一个hub里的订阅对象来订阅发布。通过使用一个spokeID,移动设备匹配spoke并对订阅的后台表进行同步。
4.&nbsp;启动同步服务器。基本上通过com.pointbase.me.sync.Server&nbsp;类的main()方法来执行。这个服务器类用于PointBase&nbsp;发布包。还有其他几个方法在不同环境中运行服务器。您可以参考PointBase文档来得到更多的细节和例子。默认情况下,服务器监听端口8124。
5.&nbsp;使用一个spokeID和在移动设备上的类&nbsp;spoke&nbsp;stub&nbsp;来初始化同步过程。
&nbsp;

图3&nbsp;UniSync同步服务器框架图解
例3&nbsp;中的类ResetServer显示了在UniSync服务器上如何创建hub和spoke:
例3&nbsp;安装同步服务器

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">manager=SyncManager.getInstance(caturl,catdriver,catuser,catpassword);
String&nbsp;dsname;
dsname=SyncDataSource.DEFAULT;

String&nbsp;hubname="Hub";
Hub&nbsp;hub=manager.createHub(hubname);

Publication&nbsp;pub;
String&nbsp;pubname;
SpokeConfig&nbsp;spoke;
Subscription&nbsp;sub;
String&nbsp;subname="SubNameCard";
String&nbsp;tablename="NAMECARD";
String[]&nbsp;tables=new&nbsp;String[]{tablename};

//&nbsp;Publish&nbsp;the&nbsp;complete&nbsp;name-card&nbsp;table
pubname="PubNameCard";
pub=hub.newPublication(pubname,dsname,tables);
hub.publish(pub);

//&nbsp;Create&nbsp;two&nbsp;spokes&nbsp;and&nbsp;subscribe&nbsp;to&nbsp;this&nbsp;publication
for(int&nbsp;i=1;i&lt;=2;i++)&nbsp;{
&nbsp;&nbsp;String&nbsp;name="Spoke"+i;
&nbsp;&nbsp;spoke=hub.createSpokeConfig(name);
&nbsp;&nbsp;spoke.savePassword("pass"+i);
&nbsp;&nbsp;sub=spoke.newSubscription(subname,SyncDataSource.DEFAULT,pubname);
&nbsp;&nbsp;spoke.subscribe(sub);
}

//&nbsp;Publish&nbsp;the&nbsp;name-card&nbsp;table&nbsp;without&nbsp;the&nbsp;picture&nbsp;column

pubname="PubNameCardNoPicture";
pub=hub.newPublication(pubname,dsname,tables);
SyncTable&nbsp;table=pub.getSyncTable(tablename);
table.dropSyncColumns(new&nbsp;String[]{"PICTURE"});
hub.publish(pub);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
//&nbsp;Create&nbsp;two&nbsp;spokes&nbsp;and&nbsp;subscribe&nbsp;to&nbsp;this&nbsp;publication
for(int&nbsp;i=3;i&lt;=4;i++)&nbsp;{
&nbsp;&nbsp;String&nbsp;name="Spoke"+i;
&nbsp;&nbsp;spoke=hub.createSpokeConfig(name);
&nbsp;&nbsp;spoke.savePassword("pass"+i);
&nbsp;&nbsp;sub=spoke.newSubscription(subname,SyncDataSource.DEFAULT,pubname);
&nbsp;&nbsp;spoke.subscribe(sub);
}
manager.close();
</td></tr></table>下面的DBManager代码片断显示了如何获得spoke&nbsp;stub&nbsp;和如何在设备上处理同步。代码中的注释解释了应用程序的同步和独立版本的不同:
例4&nbsp;通过同步服务器访问数据

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">//&nbsp;Import&nbsp;proprietary&nbsp;classes&nbsp;for&nbsp;sync
import&nbsp;com.pointbase.me.jdbc.*;

class&nbsp;DBManager&nbsp;{

&nbsp;&nbsp;//&nbsp;In&nbsp;addition&nbsp;to&nbsp;JDBC&nbsp;connection&nbsp;variables,
&nbsp;&nbsp;//&nbsp;we&nbsp;also&nbsp;need&nbsp;to&nbsp;define&nbsp;variables&nbsp;for&nbsp;sync
&nbsp;&nbsp;//&nbsp;...&nbsp;...
&nbsp;&nbsp;private&nbsp;Spoke&nbsp;spoke;
&nbsp;&nbsp;private&nbsp;String&nbsp;spokename;
&nbsp;&nbsp;private&nbsp;int&nbsp;spoke_id;
&nbsp;&nbsp;private&nbsp;int&nbsp;spoke_range_start,spoke_range_end;
&nbsp;&nbsp;final&nbsp;static&nbsp;int&nbsp;ROWS_PER_SPOKE=1&lt;&lt;16;
&nbsp;&nbsp;private&nbsp;String&nbsp;syncurl;
&nbsp;&nbsp;private&nbsp;String&nbsp;syncpassword;


&nbsp;&nbsp;private&nbsp;DBManager()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;DB&nbsp;connection&nbsp;parameters
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;...&nbsp;...
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;sync&nbsp;parameters
&nbsp;&nbsp;&nbsp;&nbsp;syncurl&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("syncurl",&nbsp;"
http://localhost:8124";);
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;spokeid&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("spokeid",&nbsp;"1");
&nbsp;&nbsp;&nbsp;&nbsp;spokename&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("spoke",&nbsp;"Spoke"+spokeid);
&nbsp;&nbsp;&nbsp;&nbsp;syncpassword&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("syncpassword",&nbsp;"pass"+spokeid);
&nbsp;&nbsp;&nbsp;&nbsp;url&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("url",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"jdbc:pointbase:micro:pbdemo"+spokeid);

&nbsp;&nbsp;&nbsp;&nbsp;connect();
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;The&nbsp;complete&nbsp;connect&nbsp;method&nbsp;using&nbsp;synchronization&nbsp;server
&nbsp;&nbsp;private&nbsp;void&nbsp;connect()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Connecting&nbsp;to&nbsp;the&nbsp;database...");

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(driver);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;database&nbsp;doesn't&nbsp;exist,&nbsp;create&nbsp;a&nbsp;new&nbsp;database
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection&nbsp;=&nbsp;DriverManager.getConnection(url,&nbsp;user,&nbsp;password);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement&nbsp;=&nbsp;connection.createStatement();


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Check&nbsp;sync&nbsp;metadata&nbsp;and&nbsp;create&nbsp;tables
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadMeta();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Create&nbsp;prepared&nbsp;statements
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createStatement();

&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;The&nbsp;complete&nbsp;newID&nbsp;method&nbsp;using&nbsp;the&nbsp;sync&nbsp;server
&nbsp;&nbsp;private&nbsp;int&nbsp;getNewID()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;statement.executeQuery(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;MAX(ID)+1&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ID&gt;="+spoke_range_start+"&nbsp;AND&nbsp;ID&lt;"+spoke_range_end);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs.next();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;id=rs.getInt(1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(rs.wasNull())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;spoke_range_start;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;id;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;Create&nbsp;table&nbsp;and&nbsp;load&nbsp;metadata&nbsp;from&nbsp;the&nbsp;sync&nbsp;hub
&nbsp;&nbsp;void&nbsp;loadMeta()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SyncManager&nbsp;manager=SyncManager.getInstance(connection);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke=manager.getSpoke(spokename);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(spoke==null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Loading&nbsp;MetaData&nbsp;from&nbsp;url&nbsp;"+syncurl+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"&nbsp;for&nbsp;spoke&nbsp;"+spokename+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"&nbsp;using&nbsp;password&nbsp;"+syncpassword);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke=manager.createSpoke(spokename);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.savePassword(syncpassword);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.saveHubURL(syncurl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.loadConfig();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.getSnapshot();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke_id&nbsp;=&nbsp;spoke.getSpokeId();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("SpokeID&nbsp;is&nbsp;"+spoke_id);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke_range_start&nbsp;=&nbsp;ROWS_PER_SPOKE&nbsp;*&nbsp;spoke_id;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke_range_end&nbsp;=&nbsp;&nbsp;spoke_range_start&nbsp;+&nbsp;ROWS_PER_SPOKE&nbsp;-&nbsp;1;
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SyncException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Synchronize&nbsp;spoke&nbsp;databases&nbsp;(mobile&nbsp;databases)&nbsp;with&nbsp;the&nbsp;hub
&nbsp;&nbsp;//&nbsp;and&nbsp;backend&nbsp;databases
&nbsp;&nbsp;void&nbsp;sync()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.sync();
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SyncException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Other&nbsp;data&nbsp;access&nbsp;methods&nbsp;are&nbsp;the&nbsp;same&nbsp;as&nbsp;the&nbsp;non-synced&nbsp;version.
}
</td></tr></table>

高可靠性移动应用程序-移动数据库和J2ME工具(二) 内容: 翻译作者:jungleguo&nbsp;&nbsp;2003-11-15
原文:&nbsp;
http://www.javaworld.com/javaworld/jw-06-2003/jw-0606-wireless-p2.html
一个应用程序例子
现在通过一个简单的例子,我们检测一下移动数据库应用程序的典型用法和关键组件。
移动联系管理器
这是一个由PointBase提供的移动联系管理器的例子。联系管理器&nbsp;contact&nbsp;manager包括在PointBase&nbsp;4.x中。为了读者方便,我已经把源代码打包成zip文件放在Resource中。如果你想编译和运行例子,你必须先从PointBase处下载适当的jar文件。
这个应用程序本身比较简单。它主要沿用了高级地址本应用程序的通用特性。例如,它允许用户存储联系人名字,地址和电话号码;提供自觉浏览和搜索接口;和后台数据库服务器同步。图1和图2分别显示了该应用程序在标准模式和同步模式下的操作。这些屏幕快照来自一个由Insignia’s&nbsp;Jeode&nbsp;PersonalJava&nbsp;VM驱动的Pocket&nbsp;PC&nbsp;和一个由J2SE驱动的Mac&nbsp;OS&nbsp;X&nbsp;膝上型电脑。相同字节代码的应用程序没有经过修改运行在许多平台上,证明了Java的威力。

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

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

设备上的数据访问

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

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">class&nbsp;DBManager&nbsp;{
&nbsp;&nbsp;//&nbsp;DBManager&nbsp;is&nbsp;a&nbsp;singleton&nbsp;class.
&nbsp;&nbsp;private&nbsp;static&nbsp;DBManager&nbsp;instance;
&nbsp;&nbsp;private&nbsp;String&nbsp;driver;
&nbsp;&nbsp;private&nbsp;String&nbsp;url;
&nbsp;&nbsp;private&nbsp;String&nbsp;user;
&nbsp;&nbsp;private&nbsp;String&nbsp;password;
&nbsp;&nbsp;private&nbsp;boolean&nbsp;delay;
&nbsp;&nbsp;private&nbsp;Connection&nbsp;connection;
&nbsp;&nbsp;private&nbsp;Statement&nbsp;statement;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;insert;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;find;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;delete;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;update;
&nbsp;&nbsp;private&nbsp;PreparedStatement&nbsp;all;

&nbsp;&nbsp;static&nbsp;DBManager&nbsp;getInstance()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(instance&nbsp;==&nbsp;null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instance&nbsp;=&nbsp;new&nbsp;DBManager();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;instance;
&nbsp;&nbsp;}

&nbsp;&nbsp;private&nbsp;DBManager()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;parameters&nbsp;from&nbsp;runtime&nbsp;properties.
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;This&nbsp;allows&nbsp;us&nbsp;to&nbsp;switch&nbsp;to&nbsp;different&nbsp;JDBC&nbsp;databases
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;without&nbsp;changing&nbsp;the&nbsp;application&nbsp;code.
&nbsp;&nbsp;&nbsp;&nbsp;Properties&nbsp;properties&nbsp;=&nbsp;ContactManager.getProperties();
&nbsp;&nbsp;&nbsp;&nbsp;driver&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("driver",&nbsp;"com.pointbase.me.jdbc.jdbcDriver");
&nbsp;&nbsp;&nbsp;&nbsp;url&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("url",&nbsp;"jdbc:pointbase:micro:pbdemo");
&nbsp;&nbsp;&nbsp;&nbsp;user&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("user",&nbsp;"PBPUBLIC");
&nbsp;&nbsp;&nbsp;&nbsp;password&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("password",&nbsp;"PBPUBLIC");
&nbsp;&nbsp;&nbsp;&nbsp;delay&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("delayread","true").equals("true");
&nbsp;&nbsp;&nbsp;&nbsp;connect();
&nbsp;&nbsp;}

&nbsp;&nbsp;private&nbsp;void&nbsp;connect()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Load&nbsp;the&nbsp;driver&nbsp;class.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(driver);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;database&nbsp;doesn't&nbsp;exist,&nbsp;create&nbsp;a&nbsp;new&nbsp;database.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection&nbsp;=&nbsp;DriverManager.getConnection(url,&nbsp;user,&nbsp;password);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Create&nbsp;template&nbsp;statement&nbsp;objects.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement&nbsp;=&nbsp;connection.createStatement();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createStatement();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;database&nbsp;is&nbsp;newly&nbsp;created,&nbsp;load&nbsp;the&nbsp;schema.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean&nbsp;newdb=initDatabase();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Load&nbsp;sample&nbsp;data&nbsp;for&nbsp;the&nbsp;new&nbsp;tables.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(newdb)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SampleDataCreator.insert(connection);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;void&nbsp;disconnect()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection.commit();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(0);
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;Create&nbsp;the&nbsp;table&nbsp;and&nbsp;load&nbsp;the&nbsp;schema.
&nbsp;&nbsp;private&nbsp;boolean&nbsp;initDatabase()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;sql&nbsp;=&nbsp;"CREATE&nbsp;TABLE&nbsp;NameCard&nbsp;(ID&nbsp;INT&nbsp;PRIMARY&nbsp;KEY,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Name&nbsp;VARCHAR(254),&nbsp;Company&nbsp;VARCHAR(254),&nbsp;Title&nbsp;VARCHAR(254),&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Address1&nbsp;VARCHAR(254),&nbsp;Address2&nbsp;VARCHAR(254),&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Phone&nbsp;VARCHAR(254),&nbsp;Email&nbsp;VARCHAR(254),&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Picture&nbsp;Binary(1000000))";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;table&nbsp;already&nbsp;exists,&nbsp;this&nbsp;will&nbsp;throw&nbsp;an&nbsp;exception.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement.executeUpdate(sql);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;This&nbsp;means&nbsp;the&nbsp;database&nbsp;already&nbsp;exists.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true;
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Ignore&nbsp;the&nbsp;error&nbsp;-&nbsp;the&nbsp;table&nbsp;already&nbsp;exists,&nbsp;which&nbsp;is&nbsp;good
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;so&nbsp;we&nbsp;don't&nbsp;need&nbsp;to&nbsp;add&nbsp;demo&nbsp;data&nbsp;later&nbsp;on.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;Create&nbsp;statement&nbsp;templates.
&nbsp;&nbsp;private&nbsp;void&nbsp;createStatement()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"INSERT&nbsp;INTO&nbsp;NameCard&nbsp;(ID,&nbsp;Name,&nbsp;Company,&nbsp;Title,&nbsp;Address1,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Address2,&nbsp;Phone,&nbsp;Email,&nbsp;Picture)&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"valueS&nbsp;(?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?,&nbsp;?)");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;*&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;(Name&nbsp;LIKE&nbsp;?)&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AND&nbsp;(Company&nbsp;LIKE&nbsp;?)&nbsp;AND&nbsp;(Title&nbsp;LIKE&nbsp;?)&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AND&nbsp;((Address1&nbsp;LIKE&nbsp;?)&nbsp;OR&nbsp;(Address2&nbsp;LIKE&nbsp;?))&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"AND&nbsp;(Phone&nbsp;LIKE&nbsp;?)&nbsp;AND&nbsp;(Email&nbsp;LIKE&nbsp;?)");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"DELETE&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;ID&nbsp;=&nbsp;?");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;update&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"UPDATE&nbsp;NameCard&nbsp;SET&nbsp;ID=?,&nbsp;Name=?,&nbsp;Company=?,&nbsp;Title=?,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Address1=?,&nbsp;Address2=?,&nbsp;Phone=?,&nbsp;Email=?,&nbsp;Picture=?&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"WHERE&nbsp;ID&nbsp;=&nbsp;?");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all&nbsp;=&nbsp;connection.prepareStatement(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;ID,&nbsp;Name,&nbsp;Company,&nbsp;Title,&nbsp;Address1,&nbsp;Address2,&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Phone,&nbsp;Email&nbsp;FROM&nbsp;NameCard");
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Other&nbsp;methods.

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

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">Vector&nbsp;findNameCardsByKeyword(String&nbsp;name,&nbsp;String&nbsp;company,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;title,&nbsp;String&nbsp;address1,&nbsp;String&nbsp;address2,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;phone,&nbsp;String&nbsp;email)&nbsp;{
&nbsp;&nbsp;Vector&nbsp;NameCards&nbsp;=&nbsp;new&nbsp;Vector();
&nbsp;&nbsp;String[]&nbsp;keywords&nbsp;=&nbsp;{name,&nbsp;company,&nbsp;title,&nbsp;address1,&nbsp;address2,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;phone,&nbsp;email};
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;keywords.length;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;criteria&nbsp;=&nbsp;(keywords[i].equals(""))&nbsp;?&nbsp;"%"&nbsp;:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"%"&nbsp;+&nbsp;keywords[i]&nbsp;+&nbsp;"%";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find.setString(i&nbsp;+&nbsp;1,&nbsp;criteria);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;resultSet&nbsp;=&nbsp;find.executeQuery();
&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(resultSet.next())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NameCard&nbsp;nameCard&nbsp;=&nbsp;new&nbsp;NameCard(resultSet.getInt(1),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(2),&nbsp;resultSet.getString(3),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(4),&nbsp;resultSet.getString(5),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(6),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resultSet.getString(7),&nbsp;resultSet.getString(8));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!delay)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadPicture(nameCard);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NameCards.addElement(nameCard);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
&nbsp;&nbsp;return&nbsp;NameCards;
}

void&nbsp;addNameCard(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;nameCard.setID(getNewID());
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;insert.setInt(1,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(2,&nbsp;nameCard.getName());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(3,&nbsp;nameCard.getCompany());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(4,&nbsp;nameCard.getTitle());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(5,&nbsp;nameCard.getAddress1());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(6,&nbsp;nameCard.getAddress2());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(7,&nbsp;nameCard.getPhone());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setString(8,&nbsp;nameCard.getEmail());
&nbsp;&nbsp;&nbsp;&nbsp;insert.setBytes(9,&nbsp;nameCard.getPicture().getBytes());
&nbsp;&nbsp;&nbsp;&nbsp;insert.executeUpdate();
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

void&nbsp;updateNameCard(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;update.setInt(1,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(2,&nbsp;nameCard.getName());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(3,&nbsp;nameCard.getCompany());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(4,&nbsp;nameCard.getTitle());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(5,&nbsp;nameCard.getAddress1());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(6,&nbsp;nameCard.getAddress2());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(7,&nbsp;nameCard.getPhone());
&nbsp;&nbsp;&nbsp;&nbsp;update.setString(8,&nbsp;nameCard.getEmail());
&nbsp;&nbsp;&nbsp;&nbsp;update.setBytes(9,&nbsp;nameCard.getPicture().getBytes());
&nbsp;&nbsp;&nbsp;&nbsp;update.setInt(10,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;update.executeUpdate();
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

void&nbsp;deleteNameCard(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;delete.setInt(1,&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;delete.executeUpdate();
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

void&nbsp;loadPicture(NameCard&nbsp;nameCard)&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;resultSet&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement.executeQuery(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;Picture&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;ID&nbsp;=&nbsp;"&nbsp;+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nameCard.getID());
&nbsp;&nbsp;&nbsp;&nbsp;resultSet.next();
&nbsp;&nbsp;&nbsp;&nbsp;Picture&nbsp;picture&nbsp;=&nbsp;new&nbsp;Picture();
&nbsp;&nbsp;&nbsp;&nbsp;picture.setBytes(resultSet.getBytes(1));
&nbsp;&nbsp;&nbsp;&nbsp;nameCard.setPicture(picture);
&nbsp;&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
}

private&nbsp;int&nbsp;getNewID()&nbsp;{
&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;resultSet&nbsp;=&nbsp;statement.executeQuery(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;MAX(ID)+1&nbsp;FROM&nbsp;NameCard");
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(resultSet.next())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;resultSet.getInt(1);
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;}
&nbsp;&nbsp;return&nbsp;0;
}
</td></tr></table>与后台数据库同步
类DBManager也允许应用程序开发者用PointBase&nbsp;专有UniSync引擎与后台数据库同步移动数据。不同的厂商使用不同的同步引擎,但他们的概念都是相类似的。同步过程按照如下这些步骤进行:
1.&nbsp;在后台服务器和移动设备上创建相应的数据库和表
2.&nbsp;在同步服务器上创建一个hub。这个hub包含发布信息。它指定和标识用于同步(发布)的后台表(或部分表)。
3.&nbsp;使用hub来创建spoke。spoke是在同步服务器上表示移动设备的对象。每个spoke都有一个ID。它能通过在同一个hub里的订阅对象来订阅发布。通过使用一个spokeID,移动设备匹配spoke并对订阅的后台表进行同步。
4.&nbsp;启动同步服务器。基本上通过com.pointbase.me.sync.Server&nbsp;类的main()方法来执行。这个服务器类用于PointBase&nbsp;发布包。还有其他几个方法在不同环境中运行服务器。您可以参考PointBase文档来得到更多的细节和例子。默认情况下,服务器监听端口8124。
5.&nbsp;使用一个spokeID和在移动设备上的类&nbsp;spoke&nbsp;stub&nbsp;来初始化同步过程。
&nbsp;

图3&nbsp;UniSync同步服务器框架图解
例3&nbsp;中的类ResetServer显示了在UniSync服务器上如何创建hub和spoke:
例3&nbsp;安装同步服务器

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">manager=SyncManager.getInstance(caturl,catdriver,catuser,catpassword);
String&nbsp;dsname;
dsname=SyncDataSource.DEFAULT;

String&nbsp;hubname="Hub";
Hub&nbsp;hub=manager.createHub(hubname);

Publication&nbsp;pub;
String&nbsp;pubname;
SpokeConfig&nbsp;spoke;
Subscription&nbsp;sub;
String&nbsp;subname="SubNameCard";
String&nbsp;tablename="NAMECARD";
String[]&nbsp;tables=new&nbsp;String[]{tablename};

//&nbsp;Publish&nbsp;the&nbsp;complete&nbsp;name-card&nbsp;table
pubname="PubNameCard";
pub=hub.newPublication(pubname,dsname,tables);
hub.publish(pub);

//&nbsp;Create&nbsp;two&nbsp;spokes&nbsp;and&nbsp;subscribe&nbsp;to&nbsp;this&nbsp;publication
for(int&nbsp;i=1;i&lt;=2;i++)&nbsp;{
&nbsp;&nbsp;String&nbsp;name="Spoke"+i;
&nbsp;&nbsp;spoke=hub.createSpokeConfig(name);
&nbsp;&nbsp;spoke.savePassword("pass"+i);
&nbsp;&nbsp;sub=spoke.newSubscription(subname,SyncDataSource.DEFAULT,pubname);
&nbsp;&nbsp;spoke.subscribe(sub);
}

//&nbsp;Publish&nbsp;the&nbsp;name-card&nbsp;table&nbsp;without&nbsp;the&nbsp;picture&nbsp;column

pubname="PubNameCardNoPicture";
pub=hub.newPublication(pubname,dsname,tables);
SyncTable&nbsp;table=pub.getSyncTable(tablename);
table.dropSyncColumns(new&nbsp;String[]{"PICTURE"});
hub.publish(pub);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
//&nbsp;Create&nbsp;two&nbsp;spokes&nbsp;and&nbsp;subscribe&nbsp;to&nbsp;this&nbsp;publication
for(int&nbsp;i=3;i&lt;=4;i++)&nbsp;{
&nbsp;&nbsp;String&nbsp;name="Spoke"+i;
&nbsp;&nbsp;spoke=hub.createSpokeConfig(name);
&nbsp;&nbsp;spoke.savePassword("pass"+i);
&nbsp;&nbsp;sub=spoke.newSubscription(subname,SyncDataSource.DEFAULT,pubname);
&nbsp;&nbsp;spoke.subscribe(sub);
}
manager.close();
</td></tr></table>下面的DBManager代码片断显示了如何获得spoke&nbsp;stub&nbsp;和如何在设备上处理同步。代码中的注释解释了应用程序的同步和独立版本的不同:
例4&nbsp;通过同步服务器访问数据

<table style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>

<tr>
<td style="WORD-WRAP: break-word">//&nbsp;Import&nbsp;proprietary&nbsp;classes&nbsp;for&nbsp;sync
import&nbsp;com.pointbase.me.jdbc.*;

class&nbsp;DBManager&nbsp;{

&nbsp;&nbsp;//&nbsp;In&nbsp;addition&nbsp;to&nbsp;JDBC&nbsp;connection&nbsp;variables,
&nbsp;&nbsp;//&nbsp;we&nbsp;also&nbsp;need&nbsp;to&nbsp;define&nbsp;variables&nbsp;for&nbsp;sync
&nbsp;&nbsp;//&nbsp;...&nbsp;...
&nbsp;&nbsp;private&nbsp;Spoke&nbsp;spoke;
&nbsp;&nbsp;private&nbsp;String&nbsp;spokename;
&nbsp;&nbsp;private&nbsp;int&nbsp;spoke_id;
&nbsp;&nbsp;private&nbsp;int&nbsp;spoke_range_start,spoke_range_end;
&nbsp;&nbsp;final&nbsp;static&nbsp;int&nbsp;ROWS_PER_SPOKE=1&lt;&lt;16;
&nbsp;&nbsp;private&nbsp;String&nbsp;syncurl;
&nbsp;&nbsp;private&nbsp;String&nbsp;syncpassword;


&nbsp;&nbsp;private&nbsp;DBManager()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;DB&nbsp;connection&nbsp;parameters
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;...&nbsp;...
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;sync&nbsp;parameters
&nbsp;&nbsp;&nbsp;&nbsp;syncurl&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("syncurl",&nbsp;"
http://localhost:8124";);
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;spokeid&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("spokeid",&nbsp;"1");
&nbsp;&nbsp;&nbsp;&nbsp;spokename&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("spoke",&nbsp;"Spoke"+spokeid);
&nbsp;&nbsp;&nbsp;&nbsp;syncpassword&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("syncpassword",&nbsp;"pass"+spokeid);
&nbsp;&nbsp;&nbsp;&nbsp;url&nbsp;=
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.getProperty("url",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"jdbc:pointbase:micro:pbdemo"+spokeid);

&nbsp;&nbsp;&nbsp;&nbsp;connect();
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;The&nbsp;complete&nbsp;connect&nbsp;method&nbsp;using&nbsp;synchronization&nbsp;server
&nbsp;&nbsp;private&nbsp;void&nbsp;connect()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Connecting&nbsp;to&nbsp;the&nbsp;database...");

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(driver);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;If&nbsp;the&nbsp;database&nbsp;doesn't&nbsp;exist,&nbsp;create&nbsp;a&nbsp;new&nbsp;database
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connection&nbsp;=&nbsp;DriverManager.getConnection(url,&nbsp;user,&nbsp;password);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;statement&nbsp;=&nbsp;connection.createStatement();


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Check&nbsp;sync&nbsp;metadata&nbsp;and&nbsp;create&nbsp;tables
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;loadMeta();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Create&nbsp;prepared&nbsp;statements
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createStatement();

&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.exit(1);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;The&nbsp;complete&nbsp;newID&nbsp;method&nbsp;using&nbsp;the&nbsp;sync&nbsp;server
&nbsp;&nbsp;private&nbsp;int&nbsp;getNewID()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;statement.executeQuery(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"SELECT&nbsp;MAX(ID)+1&nbsp;FROM&nbsp;NameCard&nbsp;WHERE&nbsp;"+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"ID&gt;="+spoke_range_start+"&nbsp;AND&nbsp;ID&lt;"+spoke_range_end);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rs.next();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;id=rs.getInt(1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(rs.wasNull())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;spoke_range_start;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;id;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
&nbsp;&nbsp;}

&nbsp;&nbsp;//&nbsp;Create&nbsp;table&nbsp;and&nbsp;load&nbsp;metadata&nbsp;from&nbsp;the&nbsp;sync&nbsp;hub
&nbsp;&nbsp;void&nbsp;loadMeta()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SyncManager&nbsp;manager=SyncManager.getInstance(connection);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke=manager.getSpoke(spokename);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(spoke==null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Loading&nbsp;MetaData&nbsp;from&nbsp;url&nbsp;"+syncurl+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"&nbsp;for&nbsp;spoke&nbsp;"+spokename+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"&nbsp;using&nbsp;password&nbsp;"+syncpassword);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke=manager.createSpoke(spokename);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.savePassword(syncpassword);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.saveHubURL(syncurl);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.loadConfig();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.getSnapshot();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke_id&nbsp;=&nbsp;spoke.getSpokeId();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("SpokeID&nbsp;is&nbsp;"+spoke_id);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke_range_start&nbsp;=&nbsp;ROWS_PER_SPOKE&nbsp;*&nbsp;spoke_id;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke_range_end&nbsp;=&nbsp;&nbsp;spoke_range_start&nbsp;+&nbsp;ROWS_PER_SPOKE&nbsp;-&nbsp;1;
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SyncException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Synchronize&nbsp;spoke&nbsp;databases&nbsp;(mobile&nbsp;databases)&nbsp;with&nbsp;the&nbsp;hub
&nbsp;&nbsp;//&nbsp;and&nbsp;backend&nbsp;databases
&nbsp;&nbsp;void&nbsp;sync()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spoke.sync();
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(SyncException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;//&nbsp;Other&nbsp;data&nbsp;access&nbsp;methods&nbsp;are&nbsp;the&nbsp;same&nbsp;as&nbsp;the&nbsp;non-synced&nbsp;version.
}
</td></tr></table>

摘要:
翻译作者:jungleguo&nbsp;&nbsp;2003-11-15
原文:&nbsp;↑返回目录
前一篇: J2ME Game开发笔记(第二辑)
后一篇: j2me网络实战指南