前言
很多同學(xué)在 API 接口開發(fā)過程中肯定遇到過這樣的問題:如果 API 輸出的內(nèi)容直接使用 DB 表實體類,可能實際需求還得附帶更多的其他業(yè)務(wù)字段信息。如果新建一個類,包含 DB 表實體類字段信息,并帶上其他業(yè)務(wù)信息,是不是感覺又很累贅,字段屬性拷貝更是令人頭疼。因此,很有必要考慮用統(tǒng)一的一套方案,來優(yōu)雅的解決這個困擾。
DO、VO 實體類定義
DO 定義,沒什么好說的,跟 DB 表字段一一對應(yīng)。
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;
}
類型轉(zhuǎn)換器定義
二話不說,先定一個類型轉(zhuǎn)換器接口
package com.yb.demo.converter;
import com.yb.demo.common.exception.ConverterException;
import java.util.List;
/**
* 對象轉(zhuǎn)換器
*
* @author daoshenzzg@163.com
* @date 2019-09-09 10:20
*/
public interface Converter<DO, VO> {
/**
* 對象轉(zhuǎn)換
*
* @param from
* @param clazz
* @return
*/
VO convert(DO from, Class<VO> clazz) throws ConverterException;
/**
* 對象批量轉(zhuǎn)換
*
* @param fromList
* @param clazz
* @return
*/
List<VO> convert(List<DO> fromList, Class<VO> clazz) throws ConverterException;
}
然后定一個基礎(chǔ)的轉(zhuǎn)換器來實現(xiàn)它。這里的默認實現(xiàn)是使用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;
/**
* 默認基礎(chǔ)對象轉(zhuǎn)換器
*
* @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 業(yè)務(wù)自定義來繼承 BaseConverter 。然后在使用super的默認轉(zhuǎn)換器實現(xiàn)拷貝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;
/**
* 可以自定義轉(zhuǎn)換,也可以直接用BaseConverter的默認實現(xiàn)
*
* @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);
// 模擬批量通過學(xué)生ID找到對應(yīng)的老師
if (!CollectionUtils.isEmpty(voList)) {
voList.forEach(entry -> entry.setTeacherName("莫言"));
}
return voList;
}
}
StudentConverter 轉(zhuǎn)換器定義完了,然后在業(yè)務(wù)代碼中使用
@Autowired
private StudentConverter studentConverter;
/**
* 學(xué)生列表
*
* @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
}
建議:如果有同學(xué)是做 API 輸出,需要追求極致的性能,可以自己在業(yè)務(wù)converter自己去實現(xiàn)拷貝方式。但,像一些管理后臺,也不太追求性能,完全可以直接使用BaseConverter的屬性拷貝方式。
結(jié)束語
這樣去定義,不僅代碼耦合度低,你會發(fā)現(xiàn),大伙都會使用這種方式去實現(xiàn)。代碼高度統(tǒng)一。