Null Object pattern with a MyBatis TypeHandler

Deadly 提交于 2019-12-10 15:54:49

问题


I have been trying to make a custom TypeHandler in MyBatis so that for a null column in the database, MyBatis returns an implementation of a Null Object pattern instead of having a null in the domain class.

After googling for help, I reached the excelent project mybatis-koans, namely the koan 19 that addresses exactly this issue with the same approach I am using, i.e., extending BaseTypeHandler<T> (is abstract). At this point, I have a concrete TypeHandler similar to the EmailTypeHandler in that koan:

/**
 * Acts as a factory method to return the appropriate implementation of an Email.
 * Returns a Null object if the email value in the database was null/empty
 */
public class EmailTypeHandler extends BaseTypeHandler<Email> {

  @Override
  public Email getNullableResult(ResultSet rs, String colName) throws SQLException {
    return createEmail(rs.getString(colName));
  }

  @Override
  public Email getNullableResult(ResultSet rs, int colNum) throws SQLException {
    return createEmail(rs.getString(colNum));
  }


  private Email createEmail(String s) {
    System.out.println(s);
    if (s == null || s.equals("")) {
      return new NullEmail();
    } else {
      return new EmailImpl(s);
    }
  }

  @Override
  public Email getNullableResult(CallableStatement arg0, int arg1) throws SQLException {
    return null;
  }

  @Override
  public void setNonNullParameter(PreparedStatement ps, int colNum,
                                  Email e, JdbcType t) throws SQLException {
  }
}

Unfortunately, it seems that the author (midpeter444) is facing the same problem: when the value in the DB is null, null is still returned instead of the object crafted in the concrete TypeHandler.


回答1:


I saw the solution right after posting the question. In BaseTypeHandler code:

[...]
  public T getResult(ResultSet rs, String columnName) throws SQLException {
    T result = getNullableResult(rs, columnName);
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    T result = getNullableResult(rs, columnIndex);
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    T result = getNullableResult(cs, columnIndex);
    if (cs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }

  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
[...]

Clearly, BaseTypeHandler is going to return null when the value in the database is null, as cs.WasNull() is true in that case. Therefore, the solution is to create a concrete TypeHandler<T> returning the appropiate implementation (in this case, a NullObject implmentation when the value in the database is null) without subclassing BaseTypeHandler.

EDITED (folowing quux00's comment): Of course, we can just override getResult method of BaseTypeHandler, given that the functionality provided by its method setParameters can be useful.




回答2:


Thanks for your answer! It helped me understand my problem and how to overcome it. I wanted a result to never be null.

By you posting the code for BaseTypeHandler, it confirmed that null was being returned depending on ResultSet.wasNull and not on the result itself returned from getNullableResult. So I overrode getResult in the concrete subclass of BaseTypeHandler. In the custom implementations of getResult, super.getResult is called, and then null is checked for. Below is an example:

public Money getResult ( ResultSet rs, String columnName ) throws SQLException
{
    Money result = super.getResult ( rs, columnName );
    return result == null ? Money.ZERO : result;
}

public Money getResult ( ResultSet rs, int columnIndex ) throws SQLException
{
    Money result = super.getResult ( rs, columnIndex );
    return result == null ? Money.ZERO : result;
}

public Money getResult ( CallableStatement cs, int columnIndex ) throws SQLException
{
    Money result = super.getResult ( cs, columnIndex );
    return result == null ? Money.ZERO : result;
}


来源:https://stackoverflow.com/questions/14874547/null-object-pattern-with-a-mybatis-typehandler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!