Java MyBatis Enum string value

2020-03-19 04:19发布

I feel like this is a simple problem, but none of the things i tried work for me. I have an enum, the reason i have string constructor is because Java doesn't allow enum to be numerical..I tried AA, AB, 2C directly without string constructor but that gives an error. Note that for the existing enum i am adding C("2C").

public enum TestEnum{
      AA("AA"), AB("AB"), C("2C");
      private String display;
    private TestEnum( String display ) {
          this.display = display;
       }
    public String toString() {
          return display;
       }
    public String getDisplay() {
          return display;
       }
    public void setDisplay( String display ) {
          this.display = display;
       }
     public String getName() {
          return display;
       }

Now i have a mybatis mapper which does a merge this is existing and one of the param to the mapper is TestEnum. Until now this worked fine since enum value and string value are same, but i added C("2C"). Now i want to insert 2C to the table using mybaits, but it always inserts C.

merge into text t
        using (select #{id} as id from dual) d on (d.id = t.id)
        when matched then
        update set
        appId = #{applId},
        src = #{testEnum}

testEnum inserts C, so i changed that to #{testEnum.toString()} which gave me a there is no getter for property name toString() error. I tried #{testEnum.display} and #{testEnum.name} both of them still inserts C whereas i want it to insert 2C. Do you guys know an easier way of handling this?

I don't want to change the model object to pass String rather than TestEnum because this object is being used in many places.Is there a way this can be done in the mybatis mapper without changing model object?

Thanks for your help :)

3条回答
做个烂人
2楼-- · 2020-03-19 04:38

What you need is a TypeHandler

First, add a static method to your TestEnum to return a TestEnum given a display string:

public static TestEnum fromDisplay(String display){
    for (TestEnum v : TestEnum.values()){
        if (v.getDisplay().equals(display)){
            return v;
        }
    }
    return null;
}

Then use it to create your TypeHandler:

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 TestEnumTypeHandler extends BaseTypeHandler<TestEnum> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, TestEnum parameter, JdbcType jdbcType)
            throws SQLException {
        ps.setString(i, parameter.getDisplay());
    }

    @Override
    public TestEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return TestEnum.fromDisplay(rs.getString(columnName));
    }

    @Override
    public TestEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return TestEnum.fromDisplay(rs.getString(columnIndex));
    }

    @Override
    public TestEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return TestEnum.fromDisplay(cs.getString(columnIndex));
    }
}

Finally, register your TypeHandler in your mybatis xml:

<typeHandlers>
  <typeHandler handler="blah.blah.TestEnumTypeHandler "/>
</typeHandlers>
查看更多
我命由我不由天
3楼-- · 2020-03-19 04:44

You do not need to write any custom TypeHandler if you want to insert the value of your Enum.

The only one thing you need to do is to specify the getter method's name in your MyBatis insert.

Example:

SQL:

CREATE TABLE demo
(
    id BIGINT,
    value VARCHAR(10),
    status CHAR(1)
);

MyBatis mapper:

@Update("UPDATE demo SET status = #{status.value} WHERE id= #{uuid}")
    long updateStatus(@Param("status") Status status, @Param("uuid") String uuid);

And the Java Enum:

public enum Status {
    ACTIVE("A"),
    INACTIVE("I");

    Status(final String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

In your case you can use src = #{testEnum.display} in your SQL.

查看更多
ら.Afraid
4楼-- · 2020-03-19 04:52

In addition to @Malt Answer:

The reason why what you are trying doesn't works it's the MyBatis EnumTypeHandler by default sets name() value of the method and is marked with final so you cannot override it:

EnumTypeHandler.class (Line 38 to 44):

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
    if (jdbcType == null) {
      ps.setString(i, parameter.name());
    } else {
      ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
    }
  }

Otherwise, the enum is created from the method valueOf(type, name) which also uses the name of the enum.

@Override
  public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    String s = rs.getString(columnIndex);
    return s == null ? null : Enum.valueOf(type, s);
  }

  @Override
  public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    String s = cs.getString(columnIndex);
    return s == null ? null : Enum.valueOf(type, s);
  }

So definitely, you need to use a typeHandler specific to handle your enum which has a specific behaviour, but I would extends directly EnumTypeHandler in specific enum type handlers, instead of BaseTypeHandler (Malt answer), because you could reuse some functionality (not in your case, but maybe in others) so it handles a general enum behaviour.

查看更多
登录 后发表回答