Html5离线存储和数据同步

来源:互联网 发布:淘宝店铺客服登录 编辑:程序博客网 时间:2024/06/10 19:14

一、整体流程设计

流程细节如下:

(1)WebSQL用于存储本地数据。

(2)LocalStroage用于暂存需要同步数据。

(3)XML用于存储所有操作过程和数操作据。

(4)将localStroage的数据同步服务器(数据冲突手动操作)。

(5)通过XML将服务器数据同步本地。

(6)本地没有数据直接同步服务器。

(7)服务器没有数据直接同步本地。

二、数据结构设计

WebSQL:数据库表。格式:items(id,name,num,user)

localStroage:Json数组对象。格式:

    [  {id:(new Date()).getTime(),table:'items',op:"insert",name:' ',num:' ',user:' '},

    {id:(new Date()).getTime(),table:'items',op:"update",name:' ',num:' ',user:' '},

    {id:(new Date()).getTime(),table:'items',op:"delete",name:' ',num:' ',user:' '}}    ……]

     其中,josns[0]代表同步数据的起点,josns[josns.length-1]代表数据同步的终点

XML:

    <store dtatbase="Item">
    <op id="0"/>
    <op id="1366526718040" table="items" action="insert">
        <data name="name">测试2   </data>
        <data name="user">    </data>
        <data name="num"> 测试2</data>
        <data name="spell">T</data>
   </op>
  <op id="1366526728408" table="items" action="update">
     <data name="name">测试3</data>
   </op>
</store>

三、同步顺序图


分析说明:

(1)LastSynTime:标识用户使用情况

  -1:表示用户在该浏览器中第一次使用。然后获取服务器是否有该用户信息,如果有则直接将服务器的数据更新到本地数据库。

不等于-1:表示用户已经操作过该浏览器。然后对syndata进行判断。

(2)syndata:表示需要同步的数据

-1:表示用户当前在该浏览器中没有同步数据。然后获取服务器是否有该用户信息,如果有则直接将服务器的数据更新到本地数据库。

不等于-1:表示用户在该浏览器中有同步数据。然后在进一步判断。

(3)id_start::为syndata[0].id,表示需要同步数据中第一个数据的操作时间戳。

id_end:为syndata[syndata.length-1],表示需要同步数据中最后一个数据的操作时间戳。

ID_END:为XML中最后一个操作数据的时间戳。

(1)若ID_End<id_start,,则将本地同步数据更新服务器数据库。

(2)若ID_End>id_end,则提取上一次同步操作数的最后一个操作时间戳id_end,从服务器获取id_end到ID_End的数据操作并更新到浏览器的webBD数据库。

(3)若id_start<ID_END<id_end,则id_start到ID_END为冲突数据需要用户手工矫正,并将ID_END到id_end更新服务器。

四、系统结构


五、用户界面































六、代码设计

1、服务端

1)、XML解析(dom4j)

(1)、获取XM存储的最后一个操作数据的时间戳(ID_END)

Element opElement = (Element) document.selectSingleNode("/store/op[last()]");
return opElement.attributeValue("id");

(2)、将JSON数组转化为XML:CretateOpXml(String json, String op, String table,String ID,String user)

        Element rootElement = dom.getRootElement();
Element opElement = rootElement.addElement("op");
opElement.addAttribute("id", ID);
opElement.addAttribute("table", table);
if (op != "delete") {
if (op == "insert") {
opElement.addAttribute("action", "insert");
} else if (op == "update") {
opElement.addAttribute("action", "update");
}
String[] items = json.split(",");
for (String item : items) {
Element dataElement = opElement.addElement("data");
String[] content = item.split(":");
dataElement.addAttribute("name", content[0]);
dataElement.setText(content[1]);
}
} else {
opElement.addAttribute("action", "delete");
Element dataElement = opElement.addElement("data");
String[] content = json.split(":");
dataElement.addAttribute("name", content[0]);
dataElement.setText(content[1]);
}

(3)、将XML转换为SQL语句: String ParseSql(String id,String user)

               StringBuffer sql = new StringBuffer();
Document document = load(fileName+user+".xml");
Element opIdElement = (Element) document.selectSingleNode("/store/op[last()]");
sql.append(opIdElement.attributeValue("id")+"|");
List list = document.selectNodes("/store/op[@id>=" + id + "]");
Iterator iter = list.iterator();
int j=0;
while (iter.hasNext()) {
Element opElement = (Element) iter.next();
String table = opElement.attributeValue("table");
Iterator<Element> dataElement = opElement.elementIterator();
StringBuffer col = new StringBuffer();
StringBuffer val = new StringBuffer();
int i = 0;
while (dataElement.hasNext()) {
Element nameElemnt = (Element) dataElement.next();
if (i == 0) {
col.append(nameElemnt.attributeValue("name"));
val.append("'" + nameElemnt.getText() + "'");
} else {
col.append("," + nameElemnt.attributeValue("name"));
val.append(",'" + nameElemnt.getText() + "'");
}


i++;
}
if (j == 0) {
if (opElement.attributeValue("action").equals("insert")) {
sql.append("incao zuosert into " + table + " (" + col.toString()+ ") values (" + val.toString() + ")");
} else if (opElement.attributeValue("action").equals("update")) {
String[] colSplit= col.toString().split(",");
String[] valSplit= val.toString().split(",");
String idUpdate=null;
StringBuffer update=new StringBuffer();
for(int ii=0;ii<colSplit.length;ii++){
if(ii==1){
update.append(colSplit[ii]+"="+valSplit[ii]);
}else if(ii>1){
update.append(","+colSplit[ii]+"="+valSplit[ii]);
}else{
idUpdate=colSplit[ii]+"="+valSplit[ii];
}
}
sql.append("update "+table+" set " + update.toString()+" where "+idUpdate);
} else {// delete
sql.append("delete from " + table + " where "+ col.toString() + "=" + val.toString());
}
}else{
if (opElement.attributeValue("action").equals("insert")) {
sql.append(";insert into " + table + " (" + col.toString() + ") values (" + val.toString() + ")");
} else if (opElement.attributeValue("action").equals("update")) {
String[] colSplit= col.toString().split(",");
String[] valSplit= val.toString().split(",");
String idUpdate=null;
StringBuffer update=new StringBuffer();
for(int ii=0;ii<colSplit.length;ii++){
if(ii==1){
update.append(colSplit[ii]+"="+valSplit[ii]);
}else if(ii>1){
update.append(","+colSplit[ii]+"="+valSplit[ii]);
}else{
idUpdate=colSplit[ii]+"="+valSplit[ii];
}
}
sql.append(";update "+table+" set " + update.toString()+" where "+idUpdate);
} else {// delete
sql.append(";delete from " + table + " where "+ col.toString() + "=" + val.toString());
}
}
j++;
}
return sql.toString();

2)、服务端数据解析(将ajax传递的JSON数组对象转换为JSON对象,同时更新数据库)

String synClientData(String userName){

Long ID=(long) 0;//Long.parseLong(OpStoreService.GetLastTime());
JSONArray array = JSONArray.fromObject(getJsonData());
StringBuffer sql=new StringBuffer();
try {
int j=0;
for (int i = 0; i < array.size(); i++) {
Map o = (Map) array.get(i);
String table=o.get("table").toString();
String clientID=o.get("ID").toString();
if(Long.parseLong(clientID)>ID){
if(o.get("op").equals("insert")){
if(j==0){
sql.append("insert into "+table+" (");
}else{
sql.append(";insert into "+table+" (");
}
Iterator iter = o.entrySet().iterator();
StringBuffer col=new StringBuffer();
StringBuffer val=new StringBuffer();
StringBuffer xml=new StringBuffer();
int k=0;
while (iter.hasNext()) {
Map.Entry<String, String> param = null;
param = (Entry<String, String>) iter.next();
if(param.getKey().equals("ID")||param.getKey().equals("op")||param.getKey().equals("table")){
continue;
}else{
if(k==0){
col.append(param.getKey());
val.append("'"+param.getValue()+"'");
xml.append(param.getKey()+":"+param.getValue());
}else{
col.append(","+param.getKey());
val.append(",'"+param.getValue()+"'");
xml.append(","+param.getKey()+":"+param.getValue());
}
}
k++;
}
j++;
OpStoreService os=new OpStoreService();
os.CretateOpXml(xml.toString(), "insert", table,clientID,userName);
sql.append(col.toString()+") values (" +val.toString()+")");
}else if(o.get("op").equals("update")){
if(j==0){
sql.append("update "+table+" set ");
}else{
sql.append(";update "+table+" set ");
}
Iterator iter = o.entrySet().iterator();
String id=null;
StringBuffer xml=new StringBuffer();
int k=0;
while (iter.hasNext()) {
Map.Entry<String, String> param = null;
param = (Entry<String, String>) iter.next();
if(param.getKey().equals("ID")||param.getKey().equals("op")||param.getKey().equals("table")){
continue;
}else{
if(param.getKey().equals("id"))
{
id=param.getValue().toString();
xml.append("id:"+id);
}else{
xml.append(","+param.getKey()+":"+param.getValue());
if(k==0){
sql.append(param.getKey()+"='"+param.getValue()+"'");
}else{
sql.append(","+param.getKey()+"='"+param.getValue()+"'");
}
k++;
}
}
}
j++;
OpStoreService os=new OpStoreService();
os.CretateOpXml(xml.toString(), "update", table,clientID,userName);
sql.append(" where id='"+id+"'");
}else{
if(j==0){
sql.append("delete from "+table+" where id='"+o.get("id").toString()+"'");
}else{
sql.append(";delete from "+table+" where id='"+o.get("id").toString()+"'");
}
OpStoreService os=new OpStoreService();
os.CretateOpXml("id:"+o.get("id").toString(), "delete", table,clientID,userName);
j++;
}
}
}
}finally{
}
return sql.toString();

}

2、浏览器

1)js数据存储结构localStorage.getItem(item,value)

username:记录登陆用户名                                       username+"LastSynTime":该用户本地操作数据的最后一次时间戳       

username+"Syn":记录该用户需要同步的              username+"LastSyn":该用户同步操作数据的最后一次时间戳        

2)用户点击同步

$("#commitSynData").bind("vclick",function(e){
if(localStorage.getItem("LastSynTime")==null){//无,若服务器有数据,则获取全部数据
LoadServAllData();
} else{//有,根据更新时间先后对比服务器和客户端,若有数据变化,将此数据发送服务器
//判断本地数据是否变化
SendLastSynTime();
}
});

function SendLastSynTime(){
$.ajax({  
        url:'/DiscreteCourse/DoAction_SendLastSynTime.do',  
        type:'POST',  
        data:{json:LastSynTime()},  
        dataType:'json',  
        success:function (data) {
        if(data.json.trim().length<3){
        }else if(data.json.trim().length==3){
        LoadServAllData();
        }else if(data.json.trim().length==4){
        sendLoalAllData();
        }else if(data.json.trim().length==13){
        sendLocalPartData(data.json.trim());
        }else{
        var sqls=data.json.split('|');
        doSql(sqls[1]);
        upLastTime(sqls[0]);
        }
        }  
    }); 
}

2)webSQL全部更新

function doSql(sql){
var sqlItems=sql.split(";");
try {
localDB.transaction(function(transaction){
            for(var i in sqlItems){
            transaction.executeSql(sqlItems[i]);
            }
            });
        } catch (e) {
            updateStatus("很抱歉,数据库出错!");
        }
}


七、存在的不足和优化

原创粉丝点击