2012-06-22 109 views
2

我正在嘗試在我的項目工作中實現MyBatis。它是一個傳統的系統,它只使用存儲過程使用vanilla JDBC訪問數據庫。我明白要調用存儲過程,MyBatis需要一個包含存儲過程的輸入參數的對象,另一個包含結果集的對象。不知道這是否完全正確。從SP到複雜對象的映射結果

爲了防止在系統中創建太多的數據實體,我想重用現有的實體。這就是問題出現的地方。讓我解釋一下我面對的典型情況/場景,以及我如何解決它。

比方說,我在系統下面的數據實體(IES):

class Account { 
    private int accountID; 
    private String accountName; 
    private OrganizationAddress address; 
    // Getters-Setters Go Here 
} 
class OrganizationAddress extends Address { 
    // ... some attributes here 
    // Getters-Setters Go Here 
} 
class Address { 
    private String address; 
    private String city; 
    private String state; 
    private String country; 
    // Getters-Setters Go Here 
} 

我使用註解,所以我Mapper類有這樣的事情:

@Select(value = "{call Get_AccountList(#{accountType, mode=IN, jdbcType=String})}") 
@Options(statementType = StatementType.CALLABLE) 
@Results(value = { 
    @org.apache.ibatis.annotations.Result 
     (property = "accountID", column = "Account_ID"), 
    @org.apache.ibatis.annotations.Result 
     (property = "accountName", column = "Organization_Name"), 
    @org.apache.ibatis.annotations.Result 
     (property = "state", column = "State", javaType=OrganizationAddress.class) 
    }) 
List<Account> getAccountList(Param param); 

問題:當我撥打存儲過程時,Account對象的state始終爲null

爲了增加傷害,我無法訪問上述數據實體的來源。所以,我只能看不能提供這個環節上的解決辦法 - Mybatis select with nested objects

我的查詢:

  • 是否有可能對我來說,在系統中使用數據的entites已經存在,還是我要創建新的,然後將數據映射到現有的數據?
    • 如果是的話,我該怎麼辦?任何引用,如果有的話。
    • 如果否,是否有辦法減少我創建的用於調用存儲過程(用於輸入和輸出參數)的數據實體的數量?

回答

3

我覺得您的情況最好的解決方法(如果我理解正確的話)是使用MyBatis的類型處理器將狀態列映射到一個OrganizationAddress對象。

我已經根據您提供的信息和它的工作原理編寫了一個示例。下面是修改後的註釋的映射:

// Note: you have an error in the @Select line => maps to VARCHAR not "String" 
@Select(value = "{call Get_AccountList(#{accountType, mode=IN, jdbcType=VARCHAR})}") 
@Options(statementType = StatementType.CALLABLE) 
@Results(value = { 
    @org.apache.ibatis.annotations.Result 
     (property = "accountID", column = "Account_ID"), 
    @org.apache.ibatis.annotations.Result 
     (property = "accountName", column = "Organization_Name"), 
    @org.apache.ibatis.annotations.Result 
     (property = "address", column = "State", typeHandler=OrgAddressTypeHandler.class) 
    }) 
List<Account> getAccountList(Param param); 

你需要考慮的地址字段映射到「狀態」欄,並使用類型處理器與填充其「狀態」屬性創建OrganizationAddress

我創建的OrgAddressTypeHandler看起來是這樣的:

import java.sql.CallableStatement; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 

import org.apache.ibatis.type.BaseTypeHandler; 
import org.apache.ibatis.type.JdbcType; 

public class OrgAddressTypeHandler extends BaseTypeHandler<OrganizationAddress> { 

    @Override 
    public OrganizationAddress getNullableResult(ResultSet rs, String colName) throws SQLException { 
    OrganizationAddress oa = new OrganizationAddress(); 
    oa.setState(rs.getString(colName)); 
    return oa; 
    } 

    @Override 
    public OrganizationAddress getNullableResult(ResultSet rs, int colNum) throws SQLException { 
    OrganizationAddress oa = new OrganizationAddress(); 
    oa.setState(rs.getString(colNum)); 
    return oa; 
    } 

    @Override 
    public OrganizationAddress getNullableResult(CallableStatement cs, int colNum) throws SQLException { 
    OrganizationAddress oa = new OrganizationAddress(); 
    oa.setState(cs.getString(colNum)); 
    return oa; 
    } 

    @Override 
    public void setNonNullParameter(PreparedStatement arg0, int arg1, OrganizationAddress arg2, JdbcType arg3) throws SQLException { 
    // not needed for this example 
    } 
} 

如果你需要比這更完整的工作示例,我會很高興地發送更多。或者如果我誤解了你的例子,請告訴我。

使用此解決方案,您可以不加修改地使用您的域對象。您只需要TypeHandler來完成映射,而不需要XML映射器文件。

另外我用MyBatis-3做了這個。1.1在MySQL中。這裏是我創建的簡單模式和存儲過程來測試它:

DROP TABLE IF EXISTS account; 
DROP TABLE IF EXISTS organization_address; 

CREATE TABLE account (
    account_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 
    organization_name VARCHAR(45) NOT NULL, 
    account_type VARCHAR(10) NOT NULL, 
    organization_address_id SMALLINT UNSIGNED NOT NULL, 
    PRIMARY KEY (account_id) 
)ENGINE=InnoDB DEFAULT CHARSET=utf8; 

CREATE TABLE organization_address (
    organization_address_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 
    address VARCHAR(45) NOT NULL, 
    city VARCHAR(45) NOT NULL, 
    state VARCHAR(45) NOT NULL, 
    country VARCHAR(45) NOT NULL, 
    PRIMARY KEY (organization_address_id) 
)ENGINE=InnoDB DEFAULT CHARSET=utf8; 

INSERT INTO organization_address VALUES(1, '123 Foo St.', 'Foo City', 'Texas', 'USA'); 
INSERT INTO organization_address VALUES(2, '456 Bar St.', 'Bar City', 'Arizona', 'USA'); 
INSERT INTO organization_address VALUES(3, '789 Quux Ave.', 'Quux City', 'New Mexico', 'USA'); 

INSERT INTO account VALUES(1, 'Foo', 'Type1', 1); 
INSERT INTO account VALUES(2, 'Bar', 'Type1', 2); 
INSERT INTO account VALUES(3, 'Quux', 'Type2', 3); 

DROP PROCEDURE IF EXISTS Get_AccountList; 

DELIMITER $$ 

CREATE PROCEDURE Get_AccountList(IN p_account_type VARCHAR(10)) 
READS SQL DATA 
BEGIN 
    SELECT a.account_id, a.organization_name, o.state 
    FROM account a 
    JOIN organization_address o ON a.organization_address_id = o.organization_address_id 
    WHERE account_type = p_account_type 
    ORDER BY a.account_id; 
END $$ 

DELIMITER ; 
+0

是的,你已經正確理解我的問題,並且此解決方案適用於我。謝謝! :) – Rishabh