開發基礎:資料庫方法

» 資料庫方法

在這個章節中我們將示範幾個SFS2X與外部資料庫整合的範例。所有的方法都以MySQL做示範,但是你可以使用你選擇的任何其他的資料庫( 請參考HowTo tutorial)。所有的程式碼,我們都使用標準的SQL,因此可以被移植到任何其他的RDBMS。

  1. 使用資料庫的客製化登入
  2. 查詢資料庫與送出結果集合

方法#1 —使用資料庫的客製化登入

這個方法討論一個常見的場景,場景中你需要與資料庫的使用者檔案比對以驗證使用者憑證。我們還將展示如何在登錄後立即執行更多客製化的邏輯,譬如說在伺服端設定使用者變數以及加入房間。

在伺服端實現客製化登入是一個簡單的過程。SFS2X 送出下面兩個事件:

  • USER_LOGIN當客戶端請求加入一個區域時會發出這個事件。你可以在這裡驗證使用者憑證與決定使用者是否能繼續進行登入動作。在這個階段客戶端是以一個Session物件作為代表,還不是一個真正的使用者。
  • USER_JOIN_ZONE:當使用者成功加入一個區域(並且轉換成為SFS使用者(SFSUser)) 時會發出此事件。

提供的範例附帶MySQL轉儲資料,可以用來產生擴展使用的資料庫表格(下載連結在這個方法的最後部分)。下面是設定與測試範例程式的步驟。

  • 我們需要一個稱之為 sfs2x的資料庫,假如你沒有,請先建立一個。
  • 使用提供的 muppets-table.sql 檔案,匯入'muppets' 表格到資料庫中。這裡面包含了一些登入帳號可以用來測試登入。
  • 藉由複製擴展到子目錄{sfs-install-dir}/SFS2X/extensions/中以佈署擴展,你可以為子目錄選擇任何的名稱,譬如說muppetsExt。(維克:這裡的意思是要將下載範例中的TestExtension.jar檔放到{sfs-install-dir}/SFS2X/extensions/muppetsExt/ 資料夾中)
  • 在AdminTool中,選擇 Zone Configurator模件並且編輯BasicExamples 區域。為區域設定擴展,如下:
    • Name: muppetsExt (或者是你選的名字, 如果你有取不同名字的話)
    • Type: JAVA
    • Main class: sfs2x.extension.test.dblogin.DBLogin
  • 確定在Zone Configurator中的General 分頁標籤中的custom login是激活的。
  • 這時候你可以準備好重開SFS2X,除非你還需要設定資料庫連線,假如沒有事先做好這點,我們建議先參考 follow this HowTo tutorial

你現在可以啟動提供的客戶端並且設定擴展。

» 登入處理器

LoginEventHandler 類別是我們檢查憑證的地方。在這個範例中,我們存取DBManager連線,並且使用PreparedStatement 物件以建立包含參數的查詢,並且消除在外部參數中可能出現的不好字元。

// Grab a connection from the DBManager connection pool
connection = dbManager.getConnection();
 
// Build a prepared statement
PreparedStatement stmt = connection.prepareStatement("SELECT pword,id FROM muppets WHERE name=?");
stmt.setString(1, userName);
 
// Execute query
ResultSet res = stmt.executeQuery();

另一個做法是,你可以使用 DBManager.executeQuery(String sql, Object[] params) 方法,類似的運作方式,只是不需要在連線等級中運作。我們不在這個範例中使用這方法的原因是因為在寫這個範例時還沒有這功能(需要SmartFoxServer 2X 版本RC1b 或是較新的)。

當錯誤發生時(譬如說錯誤的密碼),我們引發一個SFSLoginException類型的例外,並且提供一個額外的錯誤碼:

if (!getApi().checkSecurePassword(session, dbPword, cryptedPass))
{
    SFSErrorData data = new SFSErrorData(SFSErrorCode.LOGIN_BAD_PASSWORD);
    data.addParameter(userName);
     
    throw new SFSLoginException("Login failed for user: "  + userName, data);
}

在錯誤的使用者名稱或是錯誤的密碼的例子中,我們也指定使用的名稱或是密碼,因此他會傳回到客戶端。

» 處理例外

當使用資料庫時,要記住的一個重要觀念是,適當的處理例外。任何資料庫的呼叫都可能拋出錯誤,假如有SQL語法問題等等。所以確定在任何例子中連線是關閉的是很重要的。否則的話將會產生洩漏,並且最終抽空連線池(connection pool)。最好的處理例外的方式如下:

try
{
    connection = dbManager.getConnection();
    // Your database code goes here
}
catch(SQLException sqle)
{
    // Here we handle the SQL failure
}
finally
{
    try
    {
        connection.close();
    }
    catch(SQLException sqle)
    {
        // It shouldn't happen, but if it does it's best to leave a trace in the logs
    }
}

finally 區塊確定在任何例子中連線都會在離開方法前被關閉,而圍著close()方法的額外try/catch的區塊處理稀少的可能性-在歸還連線時發生錯誤。

» 後登入處理器

當被通知USER_JOIN_ZONE事件時,我們已經準備好處理已經完全登錄到系統中的使用者(維克:不再以session物件代表)。ZoneJoinEventHandler 類別設定使用者變數"dbID"為來自資料庫的使用者id,餅且最後加入使用者到主要的客廳房間。

public class ZoneJoinEventHandler extends BaseServerEventHandler
{
    @Override
    public void handleServerEvent(ISFSEvent event) throws SFSException
    {
        User theUser = (User) event.getParameter(SFSEventParam.USER);
         
        // dbid is a hidden UserVariable, available only server side
        UserVariable uv_dbId = new SFSUserVariable("dbid", theUser.getSession().getProperty(DBLogin.DATABASE_ID));
        uv_dbId.setHidden(true);
         
        // The avatar UserVariable is a regular UserVariable
        UserVariable uv_avatar = new SFSUserVariable("avatar", "avatar_" + theUser.getName() + ".jpg");
         
        // Set the variables
        List<UserVariable> vars = Arrays.asList(uv_dbId, uv_avatar);
        getApi().setUserVariables(theUser, vars);
         
        // Join the user
        Room lobby = getParentExtension().getParentZone().getRoomByName("The Lobby");
         
        if (lobby == null)
            throw new SFSException("The Lobby Room was not found! Make sure a Room called 'The Lobby' exists in the Zone to make this example work correctly.");
         
        getApi().joinRoom(theUser, lobby);
    }
}

>> 下載此方法的原始檔案(維克:請至官網下載,這裡啥都沒有)<<

方法#2 — 查詢資料庫與送出結果集合

這個方法展現如何從資料庫中讀取資料並且以一個方便的格式將其傳送至客戶端中。在伺服端中的SFSDBManager.executeQuery方法提供一個有效的方式執行SQL查詢並且獲得一個基於SFSArray的資料結構,可以動態的傳送到客戶端。
要取得更多的細節,我們建議參考SFSDBManager javadoc

在這個範例中我們將在人員資料庫中取得一個列表,並且在短短的幾行程式碼中將它們傳送到客戶端。

附帶MySQL轉儲檔案的來源可以用來產生擴展使用的資料庫的表。(下載連結在此方法的底部有提供)。下面是設定與測試這個範例應用程式的步驟:

  • 你需要一個名為sfs2x的資料庫,假如沒有的話請建立一個。
  • 使用提供的people-table.sql檔案,匯入'people' 表到資料庫中。
  • 複製到目錄中以進行部屬{sfs-install-dir}/SFS2X/extensions/。 可以為子目錄選擇任何的名稱,譬如說peopleExt
  • 在AdminTool中,選擇 Zone Configurator模件並且編輯BasicExamples 區域。為區域設定擴展,如下:
    • Name: peopleExt (或者是你選的名字, 如果你有取不同名字的話)
    • Type: JAVA
    • Main class: sfs2x.extension.test.people.PeopleExtension
  • 確定在Zone Configurator中的General 分頁標籤中的custom login是關閉的。
  • 這時候你可以準備好重開SFS2X,除非你還需要設定資料庫連線,假如沒有事先做好這點,我們建議先參考 follow this HowTo tutorial

你現在可以啟動提供的客戶端並且設定擴展。

»請求處理器

在原始碼中你會發現一個GetPeopleHandler類別,此類別會響應由客戶端來的"getPeople"請求。讓我們看一下這如何運作:

public class GetPeopleHandler extends BaseClientRequestHandler
{
    @Override
    public void handleClientRequest(User sender, ISFSObject params)
    {
        IDBManager dbManager = getParentExtension().getParentZone().getDBManager();
        String sql = "SELECT * FROM people";
         
        try
        {
            // Obtain a resultset
            ISFSArray res = dbManager.executeQuery(sql, new Object[] {});
             
            // Populate the response parameters
            ISFSObject response = new SFSObject();
            response.putSFSArray("people", res);
             
            // Send back to requester
            send("getPeople", response, sender);
        }
        catch (SQLException e)
        {
            trace(ExtensionLogLevel.WARN, "SQL Failed: " + e.toString());
        }
    }
}

傳回的SFSArray包含了所有的以SFSObject(s)表示的紀錄,這可以讓取得欄位值變得非常簡單。下面程式碼展現出當EXTENSION_RESPONSE事件被處理時,在客戶端中(ActionScript 3)它是如何完成的:

private function onExtensionResponse(evt:SFSEvent):void
{
    var params:ISFSObject = evt.params.params as ISFSObject
    var peopleArray:ISFSArray = params.getSFSArray("people")
     
    var dump:String = "PEOPLE LIST RECEIVED:\n\n"
     
    for (var i:int = 0; i < peopleArray.size(); i++)
    {
        var item:ISFSObject = peopleArray.getSFSObject(i)
        dump += "    > " + item.getUtfString("name") + ", " + item.getUtfString("location") + ", " + item.getUtfString("occupation") + "\n"
    }
     
    dTrace(dump)
    dTrace("Total records: " + peopleArray.size())
}

我們要做的事情是用迴圈造訪SFSArray的每個元素。每個項目都是一個SFSObject,代表著結果集合的一個列,並且允許我們由名稱存取欄位。在我們的例子中,欄位名稱是:namelocationoccupation

>> 下載此方法的原始檔案(維克:請至官網下載,這裡啥都沒有)<<

上一篇:開發基礎:SFSObject 與 SFSArray

下一篇:開發基礎:第一個擴展

 
 

  按個讚!~支持本站!~

FB推薦載入中