資料的儲存介質
flat-file entity storage
不同的entity可能存放在不同的目錄,每個entity就是目錄下的一個檔案,名字是代理鍵( surrogate key),也就是索引,方便定位。檔案格式多樣,可能是直接Java對象序列化,可能是XML,JSON。
讀寫entity,需要開啟檔案和關閉檔案,因此效率低下,且不支援代理鍵之外的索引。一般用於儲存少量且基本的資料,例如應用配置。 structured file storage
結構化檔案將一個檔案是一個entity type,裡面具有這個類型的多個(所有)entity的資料。在根據代理鍵SK讀取某個entity時效能會有些問題,通常會另外有一個小的index檔案,來協助快速在大的資料檔案中定位。
這和關係型資料庫中的ISAM(Indexed Sequential Access Method)很相似,而早期的一些資料庫就是這樣實現的,如Btrieve。只是在結構化檔案中結構是私人,並由程式碼來解析,而資料庫是通用的;更重要的,關係型資料庫具有不同entity之間的關聯,而結構化檔案不能提供。如果我們在MySQL中引擎採用了ISAM,表格是不支援外鍵的。 Relational Database systems
關聯式資料庫是最常使用的資料庫,如MySQL。entity作為record存放在table中,table就是一個entity type(當然,複雜的entity可能需要多個表格),表格由嚴格的schema來定義名字,類型,限制,key。使用ANSI標準的Structured Query Language (SQL)來動作表格schema和資料。然而,沒有一種關係型資料庫是完全遵守ANSI標準,大多由自己的擴充,這使得在不同的關係型資料庫中遷移存在困難。
在Java中使用JDBC(Java Database Connectivity),為了避免每次訪問資料庫,都重新開啟和關閉資料庫的串連,第三方jar提供了串連池的管理,最常用的就是C3P0[1]。 Object-Oriented Databases
對象型資料庫嘗試解決物件導向和關係型資料庫的關聯問題。既想使用SQL,又想獲得對象。但魚和熊掌很難兼得。如何確保對象的繼承關係,如何有方便檢索,能否不使用SQL的私人擴充。這些都是問題,因此,雖然一直存在對象性資料庫,卻一直未能廣泛使用。 schema-less Database systems
無模式資料庫,也就是沒有嚴格的schema。NoSQL就是描述這樣的資料庫,在近年來發展很快。它解決了關係型資料庫中靈活field和對象整合等問題。 面向文檔資料庫 有很多類型的NoSQL資料庫,最常見的是面向文檔資料庫(document-oriented database),資料存放格式一般為XML,JSON(或BSON)。一個檔案對應關聯式資料庫中的record,一個collection對應關係型資料庫中的table。和關係型資料看相比,其特點是insert效率要高得多得多,但是查詢沒那麼快,但儲存的資料量大很多很多。流行的包括MongoDB,Apache Couch DB和Couchbase Server。 key-value store 以某種方式存放索引值對,類似Java的Map<String, String>,或者Map<String, List<String>>。有名的KVStore for Redis資料庫有:Apache Cassandra, Freebase, Amazon DynamoDB, memcache, Redis, Apache River, Couchbase和MongoDB。有些檔案資料庫也同時採用KVStore for Redis。 圖形資料庫 重點在對象關係。對象有屬性,以及與其他對象的關係,而關係是有屬性的。資料的儲存和表示為圖,實體間的關聯是自然的。最流行的圖形資料庫是Neo4j。
NoSQL資料庫沒有類似ANSI SQL標準,沒有NoSQL資料庫都有自己的用戶端,一方面通用性是確定,但是大多數的client都可以直接從資料庫中讀寫對象,而不需要如關係型資料庫那樣,將POJO類映射到表,將屬性對應到列。 無映射的傳統方式
MySQL的test資料庫中有一個很簡單的students表格,實際表格的列會多很多。
CREATE TABLE `students` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, `score` int(11) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8
我們使用了C3p0作為資料庫的串連管理池,以前寫過C3p0MysqlManager [2],可以利用,重點看看讀寫表格的代碼:
//【1】讀取例子,Student的POJO只有三個屬性,相對簡單,如果有十個,數十個呢,代碼就很囉嗦了。public Student getStudent(long id) throws SQLException{try(Connection connection = C3p0MysqlManager.getConnection(DATA_SOURCE_NAME);PreparedStatement ps = connection.prepareStatement("SELECT * FROM test.students WHERE `id`=?");){ps.setLong(1, id);try(ResultSet rs = ps.executeQuery()){if(!rs.next())return null;Student student = new Student();student.setId(id);student.setName(rs.getString("name"));student.setScore(rs.getInt("score"));return student;}}}//【2】增加entity的例子,特別地,在此示範了如何擷取自動增加的流水號public void addStudent( String name, int score) throws Exception{Student student = new Student();student.setName(name);student.setScore(score);try(Connection connection = C3p0MysqlManager.getConnection(DATA_SOURCE_NAME);PreparedStatement ps = connection.prepareStatement("INSERT into test.students(`name`,`score`) values(?,?)", Statement.RETURN_GENERATED_KEYS );){ ps.setNString(1, student.getName());ps.setInt(2, student.getScore());if(ps.executeUpdate() != 1)throw new Exception("Failed to insert record.");try(ResultSet r = ps.getGeneratedKeys()){if(!r.next())throw new Exception("Failed to retrieve ID.");student.setId(r.getLong(1)); //也可以是r.getLong("GENERATED_KEY"),因為r只有1列,用columnIndex更便捷}}}通過抓包觀察mysql的互動(url中設定useSSL=false),無論我們在ps中設定為Statement.RETURN_GENERATED_KEYS還是Statement.NO_GENERATED_KEYS,訊息報文是一樣的,均帶有LAST INSERT ID,只是是否允許代碼中通過ps.getGeneratedKeys()來擷取。
相關連結: 我的Professional Java for Web Applications相關文章