当前位置: > > > HTML5 - 浏览器的数据库引擎IndexDB使用详解(附样例)

HTML5 - 浏览器的数据库引擎IndexDB使用详解(附样例)

1,IndexedDB介绍
IndexedDB标准是HTML5官方认可的本地数据库解决方案。其目的不是取代服务器端数据库,它在一些特定场景下很有用:
(1)创建自给自足的离线应用
比如页面可以在有网络连接的时候从服务器端数据库获取所需要的数据,然后将数据保存到本地数据库,以便离线时访问。
(2)优化性能
一些应用使用大量的数据,如果持续地在需要时获取同样的数据,网页性能会下降。同样的,创建一个通过复杂计算生成数据的应用,如果不想浪费时间重复做同样的计算,我们可以将所有需要的数据保存到一个IndexedDB数据库中。让它成为超级定制缓存。
(3)改进本地存储
要保存浏览器会话间的数据,并在页面之间传递。如果数据结构简单并且不大,使用本地存储就能胜任。但如果数据非常巨大或者结构非常复杂,使用IndexedDB数据库来管理就会更加简单快捷。

2,浏览器对IndexedDB的支持情况
(1)IE10起支持
(2)Firefox 10、Chrome 23、Opera 15
(3)最新版的桌面版或移动版的Safari也支持IndexedDB。

3,创建并连接数据库
(1)假设我们使用数据库做一个收藏夹功能,其中保存的数据(链接对象)如下:
function LinkRecord(name, url, description) {
  this.name = name;
  this.url = url;
  this.description = description;
}
(2)创建并连接数据库代码如下:
var database;

window.onload = function () {
  //打开数据库
  var request = window.indexedDB.open("LinksDB", 1);

  request.onsuccess = function(event) {
	alert("创建/打开数据库成功。");

	//让数据库 可在任何地方访问
	database = request.result;

	//后续操作,比如显示数据库中保存的所有链接
	//showLinks();
  };

  request.onerror = function (event) {
	alert("发生错误:" + request.error);
  };

  request.onupgradeneeded = function(event) {
	alert("第一次创建数据库或者更新数据库。");
	//alert("数据库老版本为:" + event.oldVersion + " 更新后新版本为:" + event.newVersion)
	
	var db = request.result;    
	var objectStore = db.createObjectStore("Links", { keyPath: "url" });    
  }
};
代码说明:
1,database 变量定义在外面,这样就可以在代码的任何地方访问到它,便于后续的增删改查。
2,不管创建新数据库还是打开已有的数据库,都是调用 window.indexedDB 对象的 open() 函数。第一个参数是数据库名,第二个参数是版本号(全新的数据库版本号设为1)。
3,IndexedDB的一切都是异步的,所以当调用open()方法时,只是发送了一个请求。
4,为了要在任务完成时(比如上面的open()方法)响应,我们设置了三个事件处理器:处理成功结果的onsuccess、处理异常的onerror、以及处理onupgradeneeded事件的处理器。

(3)onupgradeneeded事件说明
onupgradeneeded事件比较特别,所以单独提出来说下。
它会在数据库版本不可用时触发(比如请求的数据库版本是2,而本地当前的数据库版本是1),这个时候我们可以进行升级数据库操作,比如添加一个新数据表(event.oldVersion 可以获取当前数据库版本)
也会在所请求的数据库不存在的时候触发,这种情况下浏览器会自动创建一个空数据库。

4,数据表的创建
创建数据表时只需要提供数据表的名称和关键字路径(不像传统数据库还有定义每一个字段的名称和类型),上面样例在数据库中创建了一个名为 Links 的数据表,关键字路径是url(这个匹配数据对象中相应的属性名)
var db = request.result;    
var objectStore = db.createObjectStore("Links", { keyPath: "url" });    
关键字路径表示对象中作为主关键字(主键)的属性,主关键字是唯一标识每条数据的信息。由于每条链接数据有唯一的URL,所有可以用它做关键字。
如果数据中没有明显适合做主键的属性,可以添加自增ID来作为主键,每次插入新数据时会自动生成唯一的数字。
var objectStore = db.createObjectStore("Links", { keyPath: "id", autoIncrement: true });    

5,删除整个数据库
window.indexedDB.deleteDatabase("LinksDB");

6,事务的创建
要用IndexedDB做任何事情,不论是写数据还是读数据,都必须先要创建一个事务。事务作为一个单元一起提交的一个或多个数据操作。如果事务中的任何一部分失败了,事务中的所有操作都会撤销,数据库会恢复到事务之前的状态。
调用database对象的 transaction() 方法创建事务:
var transaction = database.transaction(["Links"], "readwrite");
第一个参数:所有需要被引入这个事务的数据表的列表。这个信息让IndexedDB可以锁定数据表,以阻止其他代码同时产生重叠或可能不一致的修改。
第二个参数:指示事务类型的字符串。如果要创建一个可以对数据表进行任何操作的事务(不论是插入、更新还是删除数据),那就用 readwrite;如果只需要读取数据,那就用 readonly

7,新增、修改数据
同其他的数据操作一样:首先要创建一个事务,然后为事务获取对象存储,调用一个对象存储的方法,处理成功和错误信息。
var linkRecord = new LinkRecord("hangge.com", "http://www.hangge.com",
	"航歌 - 做最好的开发者知识平台");

var transaction = database.transaction(["Links"], "readwrite");
var objectStore = transaction.objectStore("Links");

var request = objectStore.put(linkRecord);
request.onerror = function(event) {
	alert("发生错误:" + request.error);
};

request.onsuccess = function(event) {
	alert("数据保存成功");
}; 
如果调用 put() 添加的数据与已存在的数据有相同的主关键字,浏览器会将新数据替换已存在的数据。

8,在数据表中查询所有数据
我们需要一个游标来浏览一个IndexedDB数据表。
var transaction = database.transaction(["Links"], "readonly");
var objectStore = transaction.objectStore("Links");

var request = objectStore.openCursor();

request.onerror = function (event) {
	alert("发生错误:" + request.error);
};

//准备用于显示结果的字符串
var message = "";

request.onsuccess = function (event) {    
	//创建一个游标
	var cursor = event.target.result;

	//根据游标判断是否有数据
	if (cursor) {
		var linkRecord = cursor.value;
		message +=  linkRecord.name + "  " + linkRecord.url + "\n";

		//调用cursor.continue()方法访问下一条数据
		//当游标到达下一条数据时,onsuccess事件会再一次触发
		cursor.continue();
	}
	else {
		//如果一个结果也没有,说明游标到底了,输出信息
		alert(message);
	}
}

9,查询单条数据
比获取全部数据简单,不需要使用游标,只需使用对象存储的 get() 方法即可。
var transaction = database.transaction(["Links"], "readonly");
var objectStore = transaction.objectStore("Links");

var request = objectStore.get("http://www.hangge.com");

request.onerror = function(event) {
	alert("发生错误:" + request.error);
};

request.onsuccess = function(event) {
	var linkRecord = request.result;
	var message = "获取数据成功。\n";
	message += linkRecord.name + "\n";
	message += linkRecord.url + "\n";
	message += linkRecord.description + "\n";
	alert(message);
}

10,删除一条数据
使用对象存储的 delete() 方法即可。
var transaction = database.transaction(["Links"], "readwrite");
var objectStore = transaction.objectStore("Links");

var request = objectStore.delete("http://www.hangge.com");

request.onerror = function (event) {
	alert("发生错误:" + request.error);
};

request.onsuccess = function (event) {
	alert("删除成功。");
} 

11,完整样例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>hangge.com</title>

  <style>
	body {
	  font-family: "Trebuchet MS", Helvetica, sans-serif;
	  font-size: 14px;
	  margin-left: 20px;
	  margin-right: 20px;
	}

	form {
	  margin-bottom: 30px;
	}

	fieldset {
	  margin-bottom: 15px;
	  padding: 10px;
	  max-width: 500px;
	}

	legend {
	  padding: 0px 3px;
	  font-weight: bold;
	}

	label {
	  width: 125px;
	  display: inline-block;
	  vertical-align: top;
	  margin: 6px;
	}

	input:focus {
	  background: #eaeaea;
	}

	input {
	  font-family: "Trebuchet MS", Helvetica, sans-serif;
	  width: 300px;
	}

	textarea {
	  font-family: "Trebuchet MS", Helvetica, sans-serif;
	  height: 100px;
	  width: 500px;
	}

	input[type=button] {
	  width: 150px;
	  padding: 8px;
	}

	div.resultBox {
	  margin-top: 20px;
	  padding: 10px;
	  border: 2px solid #D8EEFE;
	  border-radius: 15px;
	  max-width: 500px;
	}

	.linkButton {
	 cursor:pointer;
	 color:blue;
	 text-decoration:underline;
	}
  </style>
  <script>
	var database;

	window.onload = function () {
	  var request = window.indexedDB.open("LinksDB2", 1);

	  request.onsuccess = function(event) {
		alert("创建/打开数据库成功。");

		//让数据库 可在任何地方访问
		database = request.result;

		//显示数据库中保存的所有链接
		showLinks();
	  };

	  request.onerror = function (event) {
		alert("发生错误:" + request.error);
	  };

	  request.onupgradeneeded = function(event) {
		alert("第一次创建数据库或者更新数据库。");
		//alert("数据库老版本为:" + event.oldVersion + " 更新后新版本为:" + event.newVersion)
		
		var db = request.result;    
		var objectStore = db.createObjectStore("Links", { keyPath: "url" });    
	  }
	};

	function addLink() {  
	  //从form表单中获取数据
	  var name = document.getElementById("name").value;
	  var url = document.getElementById("url").value;
	  var description = document.getElementById("description").value;
	  
	  var linkRecord = new LinkRecord(name, url, description);
	  
	  var transaction = database.transaction(["Links"], "readwrite");
	  var objectStore = transaction.objectStore("Links");

	  var request = objectStore.put(linkRecord);
	  request.onerror = function(event) {
		alert("发生错误:" + request.error);
	  };

	  request.onsuccess = function(event) {
		alert("添加链接成功");

		//显示数据库中保存的所有链接
		showLinks();
	  };  
	}

	function showLinks() {
	  var transaction = database.transaction(["Links"], "readonly");
	  var objectStore = transaction.objectStore("Links");

	  var request = objectStore.openCursor();
	  
	  request.onerror = function (event) {
		alert("发生错误:" + request.error);
	  };
	  
	  //要在页面上显示的字符串
	  var markupToInsert = "";

	  request.onsuccess = function (event) {    
		//创建一个游标
		var cursor = event.target.result;

		//根据游标判断是否有数据
		if (cursor) {
		  var linkRecord = cursor.value;
		  markupToInsert += "<a href=" + linkRecord.url + ">" + linkRecord.name + "</a> (" +
			"<span class='linkButton' data-url='" + linkRecord.url +
			"' onclick='getLinkDetails(this)'>详细</span>" + " " +
			"<span class='linkButton' data-url='" + linkRecord.url + 
			"' onclick='deleteLink(this)'>删除</span>" +
			")<br>";

		  //调用cursor.continue()方法访问下一条数据
		  //当游标到达下一条数据时,onsuccess事件会再一次触发
		  cursor.continue();
		}
		else {
		  ////如果一个结果也没有,说明游标到底了,输出信息
		  if (markupToInsert == "") {
			markupToInsert = "<< 暂无链接。 >>";
		  }
		  else {
			markupToInsert = "<i>目前添加的链接如下: </i><br>" + markupToInsert;
		  }

		  //显示数据
		  var resultsElement = document.getElementById("links");
		  resultsElement.innerHTML = markupToInsert;

		}
	  };
	}

	function getLinkDetails(element) {
	  var url = element.getAttribute("data-url");
		
	  var transaction = database.transaction(["Links"], "readonly");
	  var objectStore = transaction.objectStore("Links");
		
	  var request = objectStore.get(url);

	  request.onerror = function(event) {
		alert("发生错误:" + request.error);
	  };

	  request.onsuccess = function(event) {
		alert("数据获取成功");
		var linkRecord = request.result;

		var resultsElement = document.getElementById("linkDetails");
		resultsElement.innerHTML = "<b>" + linkRecord.name + "</b><br>" +
		  "<b>URL:</b> " + linkRecord.url + "<br>" +
		  "<b>描述:</b> " + linkRecord.description;
	  }
	}

	function deleteLink(element) {
	  var url = element.getAttribute("data-url");

	  var transaction = database.transaction(["Links"], "readwrite");
	  var objectStore = transaction.objectStore("Links");

	  var request = objectStore.delete(url);

	  request.onerror = function (event) {
		alert("发生错误:" + request.error);
	  };

	  request.onsuccess = function (event) {
		alert("删除成功");

		//显示数据库中保存的所有链接
		showLinks();
	  }  
	}

	function LinkRecord(name, url, description) {
	  this.name = name;
	  this.url = url;
	  this.description = description;
	}
  </script>
</head>

<body>
<h1>网址收藏夹</h1>
<form action="#">
  <fieldset>
    <legend>详细信息</legend>
      <label for="name">名称:</label>
      <input id="name" placeholder="Some website"><br>
      <label for="url">URL地址:</label>
      <input id="url" placeholder="http://somesite.somedomain.com"><br>
      <label for="Description">描述:</label>
      <input id="description" placeholder="A site about something"><br>
  </fieldset>
  <div><input type="button" value="新增链接" onclick="addLink()">
  <input type="button" value="显示所有链接" onclick="showLinks()"></div>
</form>

<div class="resultBox" id="links">
  &nbsp;
</div>

<div class="resultBox" id="linkDetails">
  &nbsp;
</div>

</body>
</html>
评论0