本文在
//www.greatytc.com/p/d614ac030e7c: 在pigx-visual之外构建微服务
//www.greatytc.com/p/353793d668ab: pigx数据库操作示例
//www.greatytc.com/p/bd81ad89035a:pigx开发示范之旅:项目规划
基础上开始,本模块主要示范多对多的开发。
一、用 代码生成工具 生成客户模块相关表文件
客户模块涉及三张表,cst_customer、bas_qualification、cst_customer_qualification
其中:cst_customer、cst_customer_qualification属于customer模块,bas_qualification属于base模块,它们分别对应目录结构中的customer及base目录。
生成三张表的文件后,将生成的后端代码复制到相应目录,注意还有mpper.xml文件。
二、修改 实体 类
由于表的主键采用UUID,所以需要修改实体类的Id属性,如下
原来为:
@TableId
private Stringid;
改为:
@TableId(value="id",type=IdType.UUID)
private Stringid;
这里只修改customer、qualification实体
三、客户模块功能设计
实现客户的增删改查
增加实现客户和客户的资质一起增加,即客户表和客户的资质表一起保存,增加是时,如果客户id、客户名称已存在,则提示:客户已存在,保存失败!
删除实现客户和客户的资质一起删除
修改实现客户和客户的资质一起修改,如果修改后的客户名称已存在,则提示:客户名称重复,更新失败!
查询实现按关键字匹配客户名称、资质名称,同时资质表在前端作为下拉查询条件匹配资质名称
四、值对象设计
从需求来看,客户对象包含客户资质对象,在customer实体中只有cst_customer表的字段属性,由于customer实体是自动生成的,考虑需求变化对表结构的影响,因此不在customer实体中添加对客户资质对象的引用,单独建立一个值对象CustomerVO来负责与前端交互,增删改查操作的对象都将是CustomerVO。
package com.mycompany.mydemo.customer.vo;
import com.mycompany.mydemo.base.entity.Qualification;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class CustomerVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private String id;
/**
* 客户名称
*/
private String name;
/**
* 资质列表
*/
private List<Qualification> qualificationList;
}
在客户查询这个需求上,有两个查询条件,一个是关键字、另一个是资质名称,在前端会以对象的形式传到后端,因此后端需要一个对象接收,为保持单一性,特建立一个CustomerRVO的类来接收前端的查询条件数据。
package com.mycompany.mydemo.customer.vo;
import lombok.Data;
import java.io.Serializable;
/**
* 查询客户的查询条件VO
*/
@Data
public class CustomerRVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 关键字
*/
private String keyWord;
/**
* 资质名称
*/
private String qualificationName;
}
五、Service与Controller设计
在生成的代码中,已有三张表的Service、Controller,但在项目中,从前端操作来看,只会有客户及资质的概念,而客户资质属于客户,因此,对三个Service、Controller的职责做如下界定:
1:接入前端只有CustomerController 和 QualificationController,它们各自负责客户及资质的交互,CustomerQualificationController删除掉。
2:对于CustomerQualificationService,只负责关系表的增删改的操作,并且是批量操作,这些操作由CustomerService调用。
3:Controller只负责中转请求和返回Service执行的结果(包括异常),每一个请求后调用Service的一个方法,Service负责业务规则、业务逻辑的检查、计算及抛出异常,特别注意:如果抛出自定义异常,这个异常类必须继承RuntimeException,不能继承于Exception,否则Controller捕获不到异常,无法将异常消息以R对象的形式返回给前端。
CustomerService示例代码
package com.mycompany.mydemo.customer.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mycompany.common.exception.BussinessException;
import com.mycompany.mydemo.customer.entity.Customer;
import com.mycompany.mydemo.customer.mapper.CustomerMapper;
import com.mycompany.mydemo.customer.service.CustomerService;
import com.mycompany.mydemo.customer.service.CustomerQualificationService;
import com.mycompany.mydemo.customer.vo.CustomerRVO;
import com.mycompany.mydemo.customer.vo.CustomerVO;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.List;
/**
*
*
* @author pigx code generator
* @date 2019-05-29 14:41:33
*/
@Service
@AllArgsConstructor
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements CustomerService {
private final CustomerQualificationService customerQualificationService;
//region 自定义的方法块
/**
* 按关键字查询客户
* @param keyWord
* @return
*/
@SneakyThrows
public List<CustomerVO> getCustomerListByKeyWord(String keyWord){
return baseMapper.getCustomerListByKeyWord(keyWord);
}
/**
* 查询客户列表
* @param customerRVO
* @return
*/
@SneakyThrows
public List<CustomerVO> getCustomerList(CustomerRVO customerRVO){
return baseMapper.getCustomerList(customerRVO);
}
/**
* 新增客户及客户资质
* @param customerVO
* @return
*/
@SneakyThrows
@Transactional(rollbackFor = Exception.class)
public boolean addCustomer(CustomerVO customerVO) {
if (baseMapper.selectById(customerVO.getId()) != null){
throw new BussinessException("客户已存在,保存失败!");
}
Customer customer = new Customer();
//复制属性值
BeanUtils.copyProperties(customerVO, customer);
baseMapper.insert(customer);
//保存客户资质表
return customerQualificationService.saveCustomerQualification(customerVO);
}
/**
* 更新客户及客户资质
* @param customerVO
* @return
*/
@SneakyThrows
@Transactional(rollbackFor = Exception.class)
public boolean updateCustomer(CustomerVO customerVO) {
Customer customer = baseMapper.selectById(customerVO.getId());
if (customer == null) {
throw new BussinessException("客户不存在,更新失败!");
}
//复制属性值
BeanUtils.copyProperties(customerVO, customer);
baseMapper.updateById(customer);
//保存客户资质表
return customerQualificationService.saveCustomerQualification(customerVO);
}
//endregion
//region 重载基类的方法块
/**
* 按 Id 删除客户及客户资质
* @param id
* @return
*/
@Override
@SneakyThrows
@Transactional(rollbackFor = Exception.class)
public boolean removeById(Serializable id){
//删除客户资质
customerQualificationService.deleteByCustomerId(id);
return super.removeById(id);
}
//endregion
}
CustomerQualificationService示例
package com.mycompany.mydemo.customer.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mycompany.mydemo.customer.entity.CustomerQualification;
import com.mycompany.mydemo.customer.mapper.CustomerQualificationMapper;
import com.mycompany.mydemo.customer.service.CustomerQualificationService;
import com.mycompany.mydemo.customer.vo.CustomerVO;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
/**
*
*
* @author pigx code generator
* @date 2019-05-29 15:02:09
*/
@Service
public class CustomerQualificationServiceImpl extends ServiceImpl<CustomerQualificationMapper, CustomerQualification> implements CustomerQualificationService {
//region 自定义的方法块
/**
* 保存客户资质
* @param customerVO
* @return
*/
public boolean saveCustomerQualification(CustomerVO customerVO){
//先删除再添加
this.deleteByCustomerId(customerVO.getId());
//保存客户资质表
List<CustomerQualification> customerQualificationList = customerVO.getQualificationList()
.stream().map(qualification -> {
CustomerQualification customerQualification = new CustomerQualification();
customerQualification.setCustomerId(customerVO.getId());
customerQualification.setQualificationId(qualification.getId());
return customerQualification;
}).collect(Collectors.toList());
return this.saveBatch(customerQualificationList);
}
/**
* 按客户Id 删除客户资质
* @param customerId
* @return
*/
public boolean deleteByCustomerId(Serializable customerId){
return baseMapper.deleteByCustomerId(customerId);
}
//endregion
}
六、Mapper映射
CustomerMapper.xml
<?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.mycompany.mydemo.customer.mapper.CustomerMapper">
<resultMap id="customerVoMap" type="com.mycompany.mydemo.customer.vo.CustomerVO">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="qualificationList" ofType="com.mycompany.mydemo.base.entity.Qualification">
<id property="id" column="qualification_id"/>
<result property="name" column="qualification_name"/>
</collection>
</resultMap>
<sql id="customerQualificationSql">
select ta.id, ta.name, tc.id as qualification_id, tc.name as qualification_name
from cst_customer as ta
left join cst_customer_qualification as tb on ta.id = tb.customer_id
left join bas_qualification as tc on tb.qualification_id = tc.id
</sql>
<select id="getCustomerListByKeyWord" resultMap="customerVoMap">
<include refid="customerQualificationSql"/>
where ta.name LIKE CONCAT('%',#{keyWord},'%') or tc.name LIKE CONCAT('%',#{keyWord},'%')
</select>
<select id="getCustomerList" resultMap="customerVoMap">
<include refid="customerQualificationSql"/>
<where>
<if test="query.keyWord != null and query.keyWord != ''">
and ta.name LIKE CONCAT('%',#{query.keyWord},'%') or tc.name LIKE CONCAT('%',#{query.keyWord},'%')
</if>
<if test="query.qualificationName != null and query.qualificationName != ''">
and tc.name = #{query.qualificationName}
</if>
</where>
</select>
</mapper>
CustomerQualificationMapper.xml
<?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.mycompany.mydemo.customer.mapper.CustomerQualificationMapper">
<resultMap id="customerQualificationMap" type="com.mycompany.mydemo.customer.entity.CustomerQualification">
<result property="customerId" column="customer_id"/>
<result property="qualificationId" column="qualification_id"/>
</resultMap>
<!--根据客户Id删除该客户的资质关系-->
<delete id="deleteByCustomerId">
DELETE FROM cst_customer_qualification WHERE customer_id = #{customerId}
</delete>
</mapper>