前言
很多同学在 API 接口开发过程中肯定遇到过这样的问题:如果 API 输出的内容直接使用 DB 表实体类,可能实际需求还得附带更多的其他业务字段信息。如果新建一个类,包含 DB 表实体类字段信息,并带上其他业务信息,是不是感觉又很累赘,字段属性拷贝更是令人头疼。因此,很有必要考虑用统一的一套方案,来优雅的解决这个困扰。
DO、VO 实体类定义
DO 定义,没什么好说的,跟 DB 表字段一一对应。
package com.yb.demo.pojo.model.db1;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
/**
* @author daoshenzzg@163.com
* @date 2019-08-05 17:58
*/
@Data
@TableName("student")
public class Student1DO {
private Long id;
@Size(max = 8, message = "studName长度不能超过8")
private String studName;
@Min(value = 12, message = "年龄不能低于12岁")
private Integer studAge;
private String studSex;
@TableField(fill = FieldFill.INSERT)
private Integer createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Integer updateTime;
}
VO 就比较有意思了,只需要多定一个 teacherName,表示我们要多输出一个字段信息,然后再继承Student1DO的所有属性。
package com.yb.demo.pojo.response;
import com.yb.demo.pojo.model.db1.Student1DO;
import lombok.Data;
/**
* @author daoshenzzg@163.com
* @date 2019-10-29 11:07
*/
@Data
public class StudentVO extends Student1DO {
private String teacherName;
}
类型转换器定义
二话不说,先定一个类型转换器接口
package com.yb.demo.converter;
import com.yb.demo.common.exception.ConverterException;
import java.util.List;
/**
* 对象转换器
*
* @author daoshenzzg@163.com
* @date 2019-09-09 10:20
*/
public interface Converter<DO, VO> {
/**
* 对象转换
*
* @param from
* @param clazz
* @return
*/
VO convert(DO from, Class<VO> clazz) throws ConverterException;
/**
* 对象批量转换
*
* @param fromList
* @param clazz
* @return
*/
List<VO> convert(List<DO> fromList, Class<VO> clazz) throws ConverterException;
}
然后定一个基础的转换器来实现它。这里的默认实现是使用BeanUtils.copyProperties方式。
package com.yb.demo.converter;
import com.yb.demo.common.exception.ConverterException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 默认基础对象转换器
*
* @author daoshenzzg@163.com
* @date 2019-09-09 10:26
*/
@Slf4j
public abstract class BaseConverter<DO, VO> implements Converter<DO, VO> {
@Override
public VO convert(DO from, Class<VO> clazz) throws ConverterException {
if (from == null) {
return null;
}
try {
VO to = clazz.newInstance();
BeanUtils.copyProperties(from, to);
return to;
} catch (Exception ex) {
throw new ConverterException(ex);
}
}
@Override
public List<VO> convert(List<DO> fromList, Class<VO> clazz) throws ConverterException {
if (CollectionUtils.isEmpty(fromList)) {
return null;
}
List<VO> toList = new ArrayList<>(fromList.size());
for (DO from : fromList) {
toList.add(convert(from, clazz));
}
return toList;
}
}
定义一个 StudentConverter 业务自定义来继承 BaseConverter 。然后在使用super的默认转换器实现拷贝bean属性,再把 teacherName 批量赋值。就达到我们的目的。
package com.yb.demo.converter;
import com.yb.demo.common.exception.ConverterException;
import com.yb.demo.pojo.model.db1.Student1DO;
import com.yb.demo.pojo.response.StudentVO;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* 可以自定义转换,也可以直接用BaseConverter的默认实现
*
* @author daoshenzzg@163.com
* @date 2019-10-29 11:12
*/
@Component
public class StudentConverter extends BaseConverter<Student1DO, StudentVO> {
/*
@Autowired
private TeacherService teacherService;
*/
@Override
public StudentVO convert(Student1DO from, Class<StudentVO> clazz) throws ConverterException {
return super.convert(from, clazz);
}
@Override
public List<StudentVO> convert(List<Student1DO> fromList, Class<StudentVO> clazz) throws ConverterException {
List<StudentVO> voList = super.convert(fromList, clazz);
// 模拟批量通过学生ID找到对应的老师
if (!CollectionUtils.isEmpty(voList)) {
voList.forEach(entry -> entry.setTeacherName("莫言"));
}
return voList;
}
}
StudentConverter 转换器定义完了,然后在业务代码中使用
@Autowired
private StudentConverter studentConverter;
/**
* 学生列表
*
* @return
*/
public List<StudentVO> listStudent(String studName) {
QueryWrapper<Student1DO> queryWrapper = new QueryWrapper<>();
queryWrapper.like("stud_name", studName);
List<Student1DO> students = super.list(queryWrapper);
return studentConverter.convert(students, StudentVO.class);
}
最终效果如下:
{
"code": 200,
"msg": "OK",
"data": [
{
"id": 1,
"studName": "张三-修改",
"studAge": 23,
"studSex": "男",
"createTime": 1559724842,
"updateTime": 1559724842,
"teacherName": "莫言"
}
],
"ttl": 74
}
建议:如果有同学是做 API 输出,需要追求极致的性能,可以自己在业务converter自己去实现拷贝方式。但,像一些管理后台,也不太追求性能,完全可以直接使用BaseConverter的属性拷贝方式。
结束语
这样去定义,不仅代码耦合度低,你会发现,大伙都会使用这种方式去实现。代码高度统一。