在之前的层次分析开始编写代码
层次分析
然后由于每个人编写代码风格不同,可以根据自己情况而定,一般分2种,代码编写顺序:从实体类到controller或者从controller到实体类。
本次Demo是采用实体类到controller编写。
实体类
先写与数控库字段一一对应的实体类
base包下的基类
@Data
@ApiModel("公有字段")
public class BaseEntity {
@ApiModelProperty(value = "")
@TableId(value = "id" ,type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "创建时间")
@TableField(value = "createTime" ,fill = FieldFill.INSERT)
private Date createTime;
@ApiModelProperty(value = "修改时间")
@TableField(value = "changeTime" ,fill = FieldFill.INSERT_UPDATE)
private Date changeTime;
@TableField(value = "logicDelete",fill = FieldFill.INSERT)
@TableLogic(value = "0",delval ="1")
private Integer logicDelete;
}
entity包下自己与数据库相连的实体
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "student_db")
@ApiModel("学生实体")
public class Student extends BaseEntity {
@ApiModelProperty(value = "班级名称")
@TableField(value = "className")
private String className;
@ApiModelProperty(value = "学生姓名")
@TableField(value = "studentName")
private String studentName;
@ApiModelProperty(value = "学生性别")
@TableField(value = "studentSex")
private String studentSex;
@ApiModelProperty(value = "学生年龄")
@TableField(value = "studentAge")
private Integer studentAge;
@ApiModelProperty(value = "加入班级时间")
@TableField(value = "addClassTime")
private Date addClassTime;
}
@ApiModel("班级实体")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "class_db")
public class StudentClass extends BaseEntity {
@ApiModelProperty("班级名称")
@TableField(value = "className")
private String className;
}
然后再写前后端相互需要的数据内容实体,这个可以根据后面接口需要的参数或者返回值进行修改就行。
DTO:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("班级DTO")
public class StudentClassDTO {
@ApiModelProperty("班级名称")
private String className;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("学生DTO")
public class StudentDTO {
@ApiModelProperty(value = "学生姓名")
private String studentName;
@ApiModelProperty(value = "学生性别")
private String studentSex;
@ApiModelProperty(value = "学生年龄")
private Integer studentAge;
@ApiModelProperty(value = "班级名称")
private String className;
}
VO:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("班级VO")
public class StudentClassVO {
@ApiModelProperty("班级名称")
private String className;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("学生VO")
public class StudentVO {
@ApiModelProperty(value = "学生姓名")
private String studentName;
@ApiModelProperty(value = "学生性别")
private String studentSex;
@ApiModelProperty(value = "学生年龄")
private Integer studentAge;
@ApiModelProperty(value = "班级名称")
private String className;
@ApiModelProperty(value = "加入班级时间")
private Date addClassTime;
}
这样需要的实体类都建立完毕。
编写其他的基础配置代码
config包下面的Mybatis-plus分页基础配置和Swagger基础配置
@Configuration
@MapperScan("com.example.demo.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
/**
* Swagger使用例子
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* 配置Swagger功能配置
* @return Docket
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(true)
.groupName("学生班级接口文档")
.select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
// 配置如何通过path过滤,即这里只扫描请求以/student开头的接口
// .paths(PathSelectors.ant("/student/**"))
.build();
}
/**
* 配置文档信息
* Contact参数:name,url,email
* ApiInfo参数:标题,描述,版本,组织链接,联系人信息,许可,许可连接,扩展
* @return ApiInfo
*/
private ApiInfo apiInfo() {
Contact contact = new Contact("联系人名字", "http://xxx.xxx.com/联系人访问链接", "联系人邮箱");
return new ApiInfo(
"考核Demo",
"班级学生基础功能实习",
"v1.0",
"http://terms.service.url/组织链接",
contact,
"Apach 2.0 许可",
"许可链接",
new ArrayList<>()
);
}
}
handler包下的自动给数据库字段内容填充的配置
@Component
public class StudentHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "changeTime", Date.class, new Date());
this.strictInsertFill(metaObject, "logicDelete", Integer.class, 0);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "changeTime", Date.class, new Date());
}
}
application.properties配置文件:
#应用名称
spring.application.name=test
#数 据 源 配 置
spring.datasource.url=jdbc:mysql://localhost:3306/school_db?characterEncoding=UTF-8&serverTimezone=CTT
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#应用服务 WEB 访问端口
server.port=8080
#相关映射
mybatis-plus.mapper-locations=classpath:mappers/*xml
mybatis-plus.type-aliases-package=com.example.test.mybatis.entity
#驼峰命名
mybatis-plus.configuration.map-underscore-to-camel-case=true
#统一字节编码
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
mybatis-plus.configuration.log-impl = org.apache.ibatis.logging.stdout.StdOutImpl
mapper(dao)、mappers的相互映射代码
由StudentClassMappers.xml映射到StudentClassMapper接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.mapper.StudentClassMapper">
<select id="studentClassPage" resultType="com.example.demo.entity.StudentClass">
SELECT * FROM class_db
</select>
</mapper>
@Mapper
@Component
public interface StudentClassMapper extends BaseMapper<StudentClass> {
IPage<StudentClass> studentClassPage(Page<StudentClass> page);
}
由StudentMappers.xml映射到StudentMapper接口
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.mapper.StudentMapper">
<select id="studentPage" resultType="com.example.demo.entity.vo.StudentVO">
SELECT * FROM student_db
</select>
<update id="emptyTime" parameterType="com.example.demo.entity.Student" >
update student_db set addClassTime = null , className = null where id = #{id}
</update>
</mapper>
@Mapper
@Component
public interface StudentMapper extends BaseMapper<Student> {
IPage<StudentVO> studentPage(Page<Student> page);
void emptyTime (long id );
}
由于我是拿成品代码所以里面有自己直接编写的sql语句的方法。就是简单的Mybatis-plus无法满足业务需求而自己编写的方法。
而接下来可以编写Service或者Controller。
demo-service业务层编写
一般而言,我都是直接先创建与实体相对应的service和impl,注意这里是实体都是与数据有直接链接的实体,就是上面的Student实体和StudentClass实体。
然后再根据接口的需求写方法在这里面。
大致的整体流程图:
3层衔接完整,这样就可以避免controller直接与数据层直接交互,而产生不安全。
StudentService和StudentImpl的代码:
@Service
public interface StudentService extends IService<Student> {
/**
* 插入新学生数据
* @param studentDTO
* @return
*/
Student transferStudent(StudentDTO studentDTO);
/**
* 更新学生信息
* @param studentDTO 更新信息
* @param student 要更新的目标
* @return
*/
Student studentUpdata(StudentDTO studentDTO, Student student);
/**
* 根据id将字段className,addClassTime置为NULL
* @param id
*/
void emptyAddClassTime(long id);
}
@Service
public class StudentImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {
@Autowired
StudentClassService studentClassService;
@Autowired
StudentService studentService;
@Override
public Student transferStudent(StudentDTO studentDTO){
Student student = Student.builder()
.studentName(studentDTO.getStudentName())
.studentSex(studentDTO.getStudentSex())
.studentAge(studentDTO.getStudentAge())
.build();
if ( studentClassService.classChooseService(studentDTO.getClassName())){
student.setClassName(studentDTO.getClassName());
student.setAddClassTime(new Date());
}else {
student.setAddClassTime(null);
student.setClassName(null);
}
return student;
}
@Override
public Student studentUpdata(StudentDTO studentDTO ,Student student) {
if (student.getClassName() != studentDTO.getClassName() &&
studentClassService.classChooseService(studentDTO.getClassName())){
student.setClassName(studentDTO.getClassName());
student.setAddClassTime(new Date());
} else if (studentDTO.getClassName() != student.getClassName() && studentDTO.getClassName() == ""){
studentService.emptyAddClassTime(student.getId());
}
student.setStudentName(studentDTO.getStudentName());
student.setStudentSex(studentDTO.getStudentSex());
student.setStudentAge(studentDTO.getStudentAge());
student.setChangeTime(null);
return student;
}
@Override
public void emptyAddClassTime(long id) {
baseMapper.emptyTime(id);
}
}
StudentClassService和StudentClassImpl代码:
@Service
public interface StudentClassService extends IService<StudentClass> {
/**
* 判断班级是否存在
* @param className
* @return
*/
boolean classChooseService(String className);
/**
* 班级加入学生
* @param classId
* @param studentId
* @return
*/
StudentVO classAddStudent(long classId , long studentId);
/**
* 班级删除学生
* @param id
* @return
*/
StudentVO classDeleteStudent(long id);
}
@Service
public class StudentClassImpl extends ServiceImpl<StudentClassMapper, StudentClass> implements StudentClassService {
@Autowired
StudentService studentService;
@Autowired
StudentClassService studentClassService;
@Override
public boolean classChooseService(String className) {
boolean result = false;
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("className",className);
if ( baseMapper.selectOne(queryWrapper) != null ){
result = true;
}
return result;
}
@Override
public StudentVO classAddStudent(long classId,long studentId) {
StudentClass studentClass =studentClassService.getBaseMapper().selectById(classId);
Student student = studentService.getBaseMapper().selectById(studentId);
String className = student.getClassName();
student.setChangeTime(null);
student.setAddClassTime(null);
if (className == studentClass.getClassName()){
System.out.println("已经加入过该班级");
}else {
student.setChangeTime(new Date());
student.setAddClassTime(new Date());
student.setClassName(studentClass.getClassName());
studentService.getBaseMapper().updateById(student);
}
StudentVO studentVO = new StudentVO();
BeanUtils.copyProperties(student,studentVO);
return studentVO;
}
@Override
public StudentVO classDeleteStudent(long id) {
studentService.emptyAddClassTime(id);
Student student = studentService.getBaseMapper().selectById(id);
StudentVO studentVO = new StudentVO();
BeanUtils.copyProperties(student,studentVO);
return studentVO;
}
}
其中涉及到DTO与VO与T之间的相互转换,还有Pag< T >和Pag< VO >之间的转换,方法有很多写原生的转换或者直接借用一些工具包都可以。
本次实例采用的是hutool工具包的方法完成相应转换。
这个具体使用链接:关于转换数据实体的方法
Controller代码
StudentController和StudentClassController代码
@RestController
@Api(value = "学生相关接口")
@RequestMapping("/student")
public class StudentController {
@Autowired
StudentService studentService;
@ApiOperation("新建学生信息")
@PostMapping("/add")
public Integer add(StudentDTO studentDTO){
return studentService.getBaseMapper()
.insert(studentService.transferStudent(studentDTO));
}
@ApiOperation("学生逻辑删除")
@DeleteMapping("/delete/{id}")
public Integer delete(@PathVariable("id") Long id){
return studentService.getBaseMapper().deleteById(id);
}
@ApiOperation("更新学生数据")
@PostMapping("/update")
public StudentVO update(StudentDTO studentDTO,
@ApiParam(value = "学生id") @RequestParam("id") long id){
Student student = studentService.getBaseMapper().selectById(id);
student = studentService.studentUpdata(studentDTO, student);
studentService.getBaseMapper().updateById(student);
StudentVO studentVO = new StudentVO();
BeanUtils.copyProperties(student,studentVO);
return studentVO;
}
@ApiOperation("支持按班级名字查询学生")
@PostMapping("/list/className")
public IPage<StudentVO> selectPage(@ApiParam(value = "班级名字") @RequestParam("className") String className){
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.like("className",className);
Page<Student> page = new Page();
page = studentService.getBaseMapper().selectPage(page,queryWrapper);
Page<StudentVO> voPage = new Page<>();
BeanUtils.copyProperties(page,voPage);
List<Student> records = page.getRecords();
List<StudentVO> studentVOList = CglibUtil.copyList(records,StudentVO::new);
voPage.setRecords(studentVOList);
return voPage;
}
@ApiOperation("学生列表获取")
@PostMapping("/list")
public IPage<StudentVO> listPage(){
Page<Student> page = new Page<>();
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.like("logicDelete",0);
page = studentService.getBaseMapper().selectPage(page,queryWrapper);
Page<StudentVO> voPage = new Page<>();
BeanUtils.copyProperties(page,voPage);
List<Student> records = page.getRecords();
List<StudentVO> studentVOList = CglibUtil.copyList(records,StudentVO::new);
voPage.setRecords(studentVOList);
return voPage;
}
}
@Api(value = "班级相关接口")
@RestController
@RequestMapping("/Class")
public class StudentClassController {
@Autowired
StudentClassService studentClassService;
@ApiOperation("班级添加学生")
@PostMapping("/addStudent")
public StudentVO addStudent(@ApiParam(value = "班级id") @RequestParam("classId") long classId,
@ApiParam(value = "学生id") @RequestParam("studentId") long studentId){
return studentClassService.classAddStudent(classId,studentId);
}
@ApiOperation("班级删除学生")
@PostMapping("/deleteStudent")
public StudentVO deleteStudent(@ApiParam(value = "学生id") @RequestParam("id") long id){
return studentClassService.classDeleteStudent(id);
}
@ApiOperation("添加新班级")
@PostMapping("/add")
public Integer add(@ApiParam(value = "班级名字") @RequestParam("className") String className){
Integer result = 0;
if(studentClassService.classChooseService(className)){
}else {
StudentClass studentClass = StudentClass.builder()
.className(className).build();
result = studentClassService.getBaseMapper().insert(studentClass);
}
return result;
}
}
然后就可以运行测试了,因为采用了Swagger文档,极大的方便前端阅读接口功能,有良好的阅读性。
测试运行
默认地址:http://localhost:8080/doc.html
功能就不一一测试了,基本功能就这样完成了。
新手实践项目的不二选择。。。。。。。。。
具体代码下载
压缩包