首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 開發 2.0:擁抱 NoSQL——從關聯思維轉向領域建模

     編輯:Java 相關知識

前言:NoSQL 為何崛起?

關聯式資料庫統治資料儲存 30 多年,但無綱要(NoSQL)資料庫的流行顯示變革正在發生。RDBMS 雖為傳統 Client-Server 架構提供堅實基礎,卻難以廉價、輕鬆地擴展至多節點。在 Facebook、Twitter 等高擴展性 Web 應用時代,這是致命弱點。早期替代方案(如 OODB)未解決核心問題,而 NoSQL(Google Bigtable、Amazon SimpleDB)作為對 Web 高擴展性需求的直接回應而崛起,或許是 Web 2.0 演進下的「殺手級應用」。

思維轉換:從關聯模型到領域模型

開發者常以為使用 NoSQL 需徹底改變思維。實際上取決於初始資料建模方法:

  • 習慣先建模資料庫結構(定義表與關聯)→ 需重新思考
  • 從領域模型開始設計 → Bigtable 無綱要結構將顯得自然

非關聯資料儲存無 Join、無主鍵/外鍵概念(僅以鬆散形式存在)。若嘗試以關聯建模為基礎在 NoSQL 建模,可能以失敗告終。從領域模型出發能簡化遷移,無綱要結構的彈性更能重燃開發活力。

實體與關聯:以對象設計為主

比賽與參賽者多對多關係圖

無綱要儲存賦予先用對象設計領域模型的彈性(Grails 等現代框架自動支援)。下一步將領域映射到底層儲存,Google App Engine (GAE) 使此過程極簡單。

Gaelyk:GAE 的輕量級 Groovy 框架

Gaelyk 利用 Google 的 Entity 物件進行物件持久化:

def ticket = new Entity("ticket")
ticket.officer = params.officer
ticket.license = params.plate
ticket.issuseDate = offensedate
ticket.location = params.location
ticket.notes = params.notes
ticket.offense = params.offense

若頻繁使用實體會顯得繁瑣,更自然的選擇是建模領域對象(如 Ticket 類別)。

透過 Shard 實現擴展

Sharding 將表結構複寫至多節點,邏輯上分割資料。但跨 Shard Join 是棘手難題。無綱要儲存優勢在於無需預知一切,比關聯結構更易適應變更。不預先定義領域對象屬性,利用 Groovy 動態特性建立 GAE Entity 的領域對象代理,專注於如何查詢對象與處理關聯——這是 NoSQL 框架尚未內建的功能。

實作範例:Race 與 Runner 的多對多關聯

Model 基類:動態屬性代理


abstract class Model {
    def entity
    static def datastore = DatastoreServiceFactory.datastoreService

    public Model(params) {
        this.@entity = new Entity(this.getClass().simpleName)
        params.each { key, val -> this.setProperty(key, val) }
    }
    public Model(Entity entity) { this.@entity = entity }

    def getProperty(String name) {
        if (name == "id") return entity.key.id
        else return entity."${name}"
    }
    void setProperty(String name, value) { entity."${name}" = value }
    def save() { entity.save() }
    def getEntity(entityType, id) {
        def key = KeyFactory.createKey(entityType, id)
        return datastore.get(key)
    }
}

關鍵技巧:@entity 繞過存取器;getClass().simpleName 設定 Entity 種類;getProperty 智能處理 id。

Race 子類


class Race extends Model {
    public Race(params) { super(params) }
    def addRunner(runner) {
        if (this.@entity.runners) this.@entity.runners << runner.id
        else this.@entity.runners = [runner.id]
    }
    def getRunners() {
        return this.@entity.runners.collect { new Runner(getEntity(Runner.class.simpleName, it)) }
    }
}

Runner 子類


class Runner extends Model {
    public Runner(params) { super(params) }
    def addRace(race) {
        if (this.@entity.races) this.@entity.races << race.id
        else this.@entity.races = [race.id]
    }
    def getRaces() {
        return this.@entity.races.collect { new Race(getEntity(Race.class.simpleName, it)) }
    }
}

建立與查詢


// 建立 Race
def iparams = ["name": "Charlottesville Marathon", "date": rdate, "distance": 26.2]
def race = new Race(iparams)
race.save()

// 查詢方法
static def findByName(name) { ... }
static def findAllByName(name, pagination=10) { ... }

GAE 控制台檢視新建 Race

雙向關聯演示


def runner = new Runner([fname:"Chris", lname:"Smith", age:34])
runner.save()
race.addRunner(runner); race.save()
runner.addRace(race); runner.save()

Race 實體包含 Runners 列表 Runner 列表詳細資訊 無比賽的 Runner 參加比賽的 Runner

無綱要儲存彈性:屬性按需自動新增,無需更新結構部署。

NoSQL 的得與失

優勢劣勢
極高彈性:新增屬性無需遷移,舊實例自動為 null犧牲一致性與完整性:無級聯刪除,需自行實作完整性檢查
讀取速度極快,適合百萬用戶級應用需嚴格紀律:屬性命名不一致將帶來維護噩夢
領域視角建模,而非關聯視角缺乏成熟持久層(如 Hibernate)支援

結語

NoSQL 不會取代 RDBMS,但會補充它。無數成功工具建立於關聯資料庫之上,RDBMS 尚無過時風險。NoSQL 為物件-關聯模型提供及時替代方案,證明某些場景下不僅可行甚至更優。最適用於需高速資料檢索與擴展性的多節點 Web 應用,極佳副作用是讓開發者從領域視角而非關聯視角進行資料建模。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved