奇技淫巧:mybatis映射数据库varchar字段到DO对象的List属性上

3,402 阅读2分钟

有时DO对象和数据库表的字段并不能完全对应,比如DO使用了List属性,数据库中对应的字段却是用逗号或空格分割的字符串。为了避免专门为做字段转换而引入新的字段以及不够优雅的转换逻辑,将DO的list属性与数据库中的字符串对应起来是很有必要的一件事。

数据库表结构

数据库表用的是mysql用例库 employees 中的departments表

该表结构可以表示如下

create table departments(
    dept_no    char(4)      not null   primary key,
    dept_name  varchar(40)  not null
);

其中的默认内容是

Dept_noDept_name
d001Marketing
d002Finance
d003Human Resources
d004Production
d005Development
d006Quality Management
d007Sales
d008Research
d009Customer Service

目标

比如我们要读取最后一条 d009,我希望用 List<String> 接收 dept_name 字段,使用空格分割该字符串,那么最终拿到的 DO 对象应该是 Departments(deptNo=d009, deptName=[Customer, Service])

编码

下面的示例包含了4个类/接口

  • Departments
  • DepartmentsDao
  • DepartmentsDaoTest
  • ListStringTypeHandler
@Data
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class Departments {
    private String deptNo;
    private List<String> deptName;
}
@Mapper
public interface DepartmentsDao {

    /**
     * 根据id查询
     *
     * @param deptNo deptNo
     * @return 对象
     */
    @Select("select * from departments where dept_no = #{deptNo}")
    @Results({@Result(property = "deptNo", column = "dept_no"),
            @Result(property = "deptName", column = "dept_name",
                    typeHandler = ListStringTypeHandler.class)})
    Departments selectById(@Param("deptNo") String deptNo);

    /**
     * 根据names查询
     *
     * @param names names
     * @return 对象
     */
    @Select("select * from departments where dept_name = #{names, " +
            "typeHandler=com.yhh.play.mybatis.dao.type.handler.ListStringTypeHandler}")
    @Results({@Result(property = "deptNo", column = "dept_no"),
            @Result(property = "deptName", column = "dept_name",
                    typeHandler = ListStringTypeHandler.class)})
    Departments selectByNames(@Param("names") List<String> names);

    /**
     * 插入
     *
     * @param departments 对象
     * @return 是否成功
     */
    @Insert("insert into departments values (#{deptNo}, #{deptName," +
            "typeHandler=com.yhh.play.mybatis.dao.type.handler.ListStringTypeHandler})")
    boolean insert(Departments departments);

    /**
     * 根据deptNo删除
     *
     * @param deptNo deptNo
     * @return 是否成功
     */
    @Delete("delete from departments where dept_no = #{deptNo}")
    boolean deleteByDeptNo(@Param("deptNo") String deptNo);

}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class DepartmentsDaoTest {

    @Autowired
    private DepartmentsDao departmentsDao;

    @Test
    public void test() {
        String deptNo = "d101";
        List<String> deptName = Arrays.asList("a", "b", "c");
        Departments departments = new Departments(deptNo, deptName);
        departmentsDao.deleteByDeptNo(deptNo);
        Assert.assertTrue(departmentsDao.insert(departments));
        Assert.assertEquals(departments, departmentsDao.selectById(deptNo));
        Assert.assertEquals(departments, departmentsDao.selectByNames(deptName));
        Assert.assertTrue(departmentsDao.deleteByDeptNo(deptNo));
        Assert.assertNull(departmentsDao.selectById(deptNo));
    }
}

public class ListStringTypeHandler extends BaseTypeHandler<List<String>> {

    private static final String DELIM = " ";

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
                                    List<String> parameter, JdbcType jdbcType)
            throws SQLException {
        String value = StringUtils.collectionToDelimitedString(parameter, DELIM);
        ps.setString(i, value);
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        String value = rs.getString(columnName);
        return Arrays.asList(StringUtils.tokenizeToStringArray(value, DELIM));
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        String value = rs.getString(columnIndex);
        return Arrays.asList(StringUtils.tokenizeToStringArray(value, DELIM));
    }

    @Override
    public List<String> getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        String value = cs.getString(columnIndex);
        return Arrays.asList(StringUtils.tokenizeToStringArray(value, DELIM));
    }
}

通过以上代码示例可以知道,通过自定义typehandler 可以将 DO 对象中的List属性转为 数据库中的 varchar 类型

同样也可以将数据库中的 varchar类型 解析为 DO 对象的 List 属性

这里面 TypeHandler 就是用来处理 javaType 与 jdbcType 之间映射用的,我上面写的ListStringTypeHandler 继承了 Mybatis 提供的抽象类,需要实现4个方法,第一个方法,用于将 javaType 转为 jdbcType,后面三个方法都是用于将 jdbcType 转为 javaType。Mybatis有很多现成的 TypeHandler,但很可惜我没有找到 List 与 varchar 互转的实现类,如果哪位同学发现更好的解决方案请不吝赐教~