可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 :)
回答1:
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>
回答2:
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.
回答3:
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.