有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

从SP到复杂对象的java映射结果

我正在努力在我的工作项目中实施MyBatis。它是一个遗留系统,仅通过存储过程使用普通JDBC访问数据库。我知道要调用存储过程,MyBatis需要一个包含存储过程输入参数的对象和另一个保存结果集的对象。不确定这是否完全正确

为了防止在系统中创建过多的数据实体,我希望重用现有的实体。这就是问题所在。让我解释一下我所面临的典型情况/情景,以及我如何试图解决它

假设系统中有以下数据实体:

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

我的问题是:

  • 我是否可以使用系统中已经存在的数据实体,还是必须创建新的实体,然后将数据映射到现有的实体?
    • 如果是,我该怎么做?任何参考资料,如果有的话
    • 如果没有,有没有办法减少我为调用存储过程而创建的数据实体的数量(对于in和out参数)

共 (1) 个答案

  1. # 1 楼答案

    我认为最好的解决方案(如果我理解正确的话)是使用MyBatis TypeHandler,它将状态列映射到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);
    

    您需要将Account的address字段映射到“state”列,并使用TypeHandler创建一个OrganizationAddress,并填写其“state”属性

    我创建的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映射器文件

    我也用MySQL中的MyBatis-3.1.1实现了这一点。下面是我为测试它而创建的简单模式和存储过程:

    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 ;