阅读SSM项目之scm(二)

简介: 项目是由eclipse来编写的,我使用的开发环境是Idea,那么就需要将eclipse项目导入进去Idea中。

抽取Service层

之前我们也抽取过Service层的代码,当时候也觉得用得十分巧妙:

  • 在baseService中提供一个setBaseDao()的方法
  • 具体serviceImpl使用具体Dao的时候,通过setDao来进行注入对应的Dao对象,同时调用父类的setDao方法,让BaseService的BaseDao能够实现实例化!

然而,这次看到的baseService就用得更加巧妙了,并且设计得更加好!

如下代码:

package cn.itcast.scm.service.impl;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import cn.itcast.scm.dao.AccountMapper;
import cn.itcast.scm.dao.AccountRecordsMapper;
import cn.itcast.scm.dao.BaseMapper;
import cn.itcast.scm.dao.BuyOrderDetailMapper;
import cn.itcast.scm.dao.BuyOrderMapper;
import cn.itcast.scm.dao.GoodsMapper;
import cn.itcast.scm.dao.SupplierMapper;
import cn.itcast.scm.dao.SysParamMapper;
import cn.itcast.scm.entity.Page;
import cn.itcast.scm.service.BaseService;
public class BaseServiceImpl<T> implements BaseService<T> {
    protected  BaseMapper<T> baseMapper;
    @Autowired
    protected  SupplierMapper supplierMapper;
    @Autowired
    protected  AccountMapper accountMapper;
    @Autowired
    protected  GoodsMapper goodsMapper;
    @Autowired
    protected  BuyOrderMapper buyOrderMapper;
    @Autowired
    protected  BuyOrderDetailMapper buyOrderDetailMapper;
    @Autowired
    protected  AccountRecordsMapper accountRecordsMapper;
    @Autowired
    protected  SysParamMapper sysParamMapper;
    @PostConstruct//在构造方法后,初化前执行
    private void initBaseMapper() throws Exception{
        //完成以下逻辑,需要对研发本身进行命名与使用规范
        //this关键字指对象本身,这里指的是调用此方法的实现类(子类)
        System.out.println("=======this :"+this);
        System.out.println("=======父类基本信息:"+this.getClass().getSuperclass());
        System.out.println("=======父类和泛型的信息:"+this.getClass().getGenericSuperclass());
        ParameterizedType type =(ParameterizedType) this.getClass().getGenericSuperclass();
        //获取第一个参数的class
        Class clazz = (Class)type.getActualTypeArguments()[0];
        System.out.println("=======class:"+clazz);
        //转化为属性名(相关的Mapper子类的引用名)Supplier  supplierMapper
        String localField = clazz.getSimpleName().substring(0,1).toLowerCase()+clazz.getSimpleName().substring(1)+"Mapper";
        System.out.println("=======localField:"+localField);
        //getDeclaredField:可以使用于包括私有、默认、受保护、公共字段,但不包括继承的字段
        Field field=this.getClass().getSuperclass().getDeclaredField(localField);
        System.out.println("=======field:"+field);
        System.out.println("=======field对应的对象:"+field.get(this));
        Field baseField = this.getClass().getSuperclass().getDeclaredField("baseMapper");
        System.out.println("=======baseField:"+baseField);
        System.out.println("=======baseField对应的对象:"+baseField.get(this));
        //field.get(this)获取当前this的field字段的值。例如:Supplier对象中,baseMapper所指向的对象为其子类型SupplierMapper对象,子类型对象已被spring实例化于容器中
        baseField.set(this, field.get(this));
        System.out.println("========baseField对应的对象:"+baseMapper);
    }   
}

这个baseService并没有给出对应的setDao的方法,那它是怎么将BaseDao实例化的呢???关键就在于initBaseMapper()这个方法!

我们来看一下方法内部打印数据的内容吧:

=======this :cn.itcast.scm.service.impl.BuyOrderServiceImpl@13a739e
=======父类基本信息:class cn.itcast.scm.service.impl.BaseServiceImpl
=======父类和泛型的信息:cn.itcast.scm.service.impl.BaseServiceImpl<cn.itcast.scm.entity.BuyOrder>
=======class:class cn.itcast.scm.entity.BuyOrder
=======localField:buyOrderMapper
=======field:protected cn.itcast.scm.dao.BuyOrderMapper cn.itcast.scm.service.impl.BaseServiceImpl.buyOrderMapper
=======field对应的对象:org.apache.ibatis.binding.MapperProxy@7cc946
=======baseField:protected cn.itcast.scm.dao.BaseMapper cn.itcast.scm.service.impl.BaseServiceImpl.baseMapper
=======baseField对应的对象:null
========baseField对应的对象:org.apache.ibatis.binding.MapperProxy@7cc946

这个方法被@PostConstruct注解给修饰着

  • 于是这个方法的执行是在Servlet构造函数之后、Servlet的init()方法之前被执行!

通过我一阵的梳理,BaseDao的实例化过程是这样的:

  • 我们具体的ServiceImpl被Spring所管理着,当具体serviceImpl被Spring实例化时,会自动调用其父类也就是baseServiceImpl
  • 当发现父类baseServiceImpl有@PostConstruct注解给修饰,于是就调用initBaseMapper()
  • 这个方法首先是获取泛型的信息(例如:BuyOrder)
  • 将泛型的信息与Mapper字符串进行拼接,拼接成(BuyOrderMapper)
  • 通过反射获取baseServiceImpl的成员变量buyOrderMapper--->这个是Mapper代理的对象,从上面的输出对象就可以看出来。
  • 然后也通过反射将baseMapper进行实例化。

其实上面也是通过具体的serviceImpl来对baseDao来进行初始化,不过它这样子做的话就显得更加优雅了。并不需要在每个具体的serviceImpl使用setDao()的方式来进行实例化。

通过上面的解释、我们把注释写上,就很容易理解了。

/**
     * 每当service实例化的时候,这个方法都会被调用
     * @throws Exception
     */
    @PostConstruct
    private void initBaseMapper() throws Exception{
        //获取泛型的信息
        ParameterizedType type =(ParameterizedType) this.getClass().getGenericSuperclass();
        Class clazz = (Class)type.getActualTypeArguments()[0];
        //拼接成“泛型”Mapper字符串
        String localField = clazz.getSimpleName().substring(0,1).toLowerCase()+clazz.getSimpleName().substring(1)+"Mapper";
        //通过反射来获取成员变量的值
        Field field=this.getClass().getSuperclass().getDeclaredField(localField);
        Field baseField = this.getClass().getSuperclass().getDeclaredField("baseMapper");
        //将baseDao来进行实例化
        baseField.set(this, field.get(this));
    }

分析项目的业务

本项目主要用EASY-UI来作为前段的页面构建。这里就不一一去探究EASY-UI的用法的,当用到这个前段UI的时候再回头看吧。下面就直接分析它的具体逻辑体悟了。

用户登陆

首先,程序的入口是用户登录的界面。用户设计得比较简单,因为只用来做登录。

CREATE TABLE account
(
    acc_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
    acc_login VARCHAR(20),
    acc_name VARCHAR(20),
    acc_pass VARCHAR(20)
);

对于用户登陆而言,我们已经是非常熟悉这个业务了。只是在数据库中对比一下数据、如果存在这个数据,那么在session域中保存即可!

微信图片_20220411225033.png

基本数据的数据库表

微信图片_20220411225037.jpg

CREATE TABLE supplier
(
    sup_id INT(11) PRIMARY KEY NOT NULL,
    sup_name VARCHAR(20),
    sup_linkman VARCHAR(20),
    sup_phone VARCHAR(11),
    sup_address VARCHAR(100),
    sup_remark VARCHAR(100),
    sup_pay DECIMAL(12,2),
    sup_type VARCHAR(10)
);

微信图片_20220411225042.jpg

CREATE TABLE goods
(
    goods_Id VARCHAR(36) PRIMARY KEY NOT NULL,
    goods_name VARCHAR(20),
    goods_unit VARCHAR(10),
    goods_type VARCHAR(10),
    goods_color VARCHAR(10),
    goods_store INT(11),
    goods_limit INT(11),
    goods_commission DECIMAL(2,2),
    goods_producer VARCHAR(50),
    goods_remark VARCHAR(100),
    goods_sel_price DECIMAL(12,2),
    goods_buy_price DECIMAL(12,2)
);

微信图片_20220411225046.jpg

CREATE TABLE store_house
(
    sh_id VARCHAR(10) PRIMARY KEY NOT NULL,
    sh_name VARCHAR(20),
    sh_responsible VARCHAR(20),
    sh_phone VARCHAR(11),
    sh_address VARCHAR(50),
    sh_type VARCHAR(10),
    sh_remark VARCHAR(100)
);

上边这几张表都仅仅是CRUD的操作。

  • 上面已经说了,我们的URL都交由了baseAction来进行处理。但是我们需要增加、查询的时候可能是需要数据库中查询出来的数据的。当然了,有些是可以通过easy-UI部分的控件能从数据库中获取得到【分页数据】,可是有的地方还是需要我们手动去查询出来。

那么这个项目是这样处理的,将经常用到的数据用一张表保存起来

drop table if exists sys_param;
/*====================================系统参数表==============================*/
/*==============================================================*/
/* Table: sys_param                                             */
/*==============================================================*/
/*
create table sys_param
(
   sys_param_id         bigint  auto_increment,
   sys_param_field      varchar(50),
   sys_param_value      varchar(50),
   sys_param_text       varchar(50),
   primary key (sys_param_id)
);
*/
create table sys_param
(
   sys_param_id         bigint  auto_increment,
   sys_param_field      varchar(50),
   sys_param_value      varchar(500),
   sys_param_text       varchar(50),
   sys_param_type       varchar(2),   
   primary key (sys_param_id)
);
insert into sys_param(sys_param_field,sys_param_value,sys_param_type) values('shId','select s.sh_id as sys_param_value,s.sh_name as sys_param_text from store_house s','1');
insert into sys_param(sys_param_field,sys_param_value,sys_param_text) values('supType','1','一级供应商');
insert into sys_param(sys_param_field,sys_param_value,sys_param_text) values('supType','2','二级供应商');
insert into sys_param(sys_param_field,sys_param_value,sys_param_text) values('supType','3','普通供应商');
insert into sys_param(sys_param_field,sys_param_value,sys_param_text) values('goodsColor','1','红色');
insert into sys_param(sys_param_field,sys_param_value,sys_param_text) values('goodsColor','2','绿色');
insert into sys_param(sys_param_field,sys_param_value,sys_param_text) values('goodsColor','3','蓝色');
select * from sys_param;

上面我们可以看到,除了单单保存了一些的基本属性之外,我们来存储了SQL语句,那么我们怎么将SQL语句转成是我们的数据呢???

  • 上面就是查询仓库的地址。也就是说,当页面加载的时候,我们的地址就被查询出来了。

微信图片_20220411225050.png

那它是怎么实现这种玩意的呢??

Mapper查询表的数据

<select id="selectList" parameterType="String" resultMap="sysParamResultMap">      
      select * from sys_param
  </select>
  <!-- 查询其它表的数据,使用${value}格式,允许使用sql语句作为参数执行 -->
  <select id="selectOthreTable" parameterType="string" resultMap="sysParamResultMap">
      ${value}
  </select>

Service将数据封装到一个总的Map中。具体的做法是这样子的:

package cn.itcast.scm.service.impl;
import cn.itcast.scm.entity.SysParam;
import cn.itcast.scm.service.SysParamService;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service("sysParamService")
public class SysParamServiceImpl extends BaseServiceImpl<SysParam> implements SysParamService {
    @Override
    public Map<String, Object> selectList() {
        //查询出表中所有所有数据
        List<SysParam> sysParams = sysParamMapper.selectList("");
        //存储属性字段具体的值
        Map<String, Object> fieldMap = null;
        //最终的Map,key是属性字段,value是一个map(属性字段具体的值)
        Map<String, Object> sysParamMap = new HashMap<String, Object>();
        //遍历表中的记录,看是否有类型为1的字段数据!也就是SQL数据
        for (SysParam sysParam : sysParams) {
            if ("1".equals(sysParam.getSysParamType())) {
                String sql = sysParam.getSysParamValue();
                //执行SQL,得到查询后的记录
                List<SysParam> otherList = sysParamMapper.selectOthreTable(sql);
                fieldMap = new HashMap<>();
                /**
                 * select s.sh_id as sys_param_value,s.sh_name as sys_param_text  from store_house s
                 */
                //遍历查询后的记录,并把数据存到字段MAP
                for (SysParam otherSysParam : otherList) {
                    /**
                     * key = 仓库的具体Id
                     * value = 页面显示的仓库名称
                     */
                    fieldMap.put(otherSysParam.getSysParamValue(), otherSysParam.getSysParamText());
                }
                /**
                 * key = shId
                 * value = 存储具体数据的Map集合
                 */
                sysParamMap.put(sysParam.getSysParamField(), fieldMap);
            } else {
                //判断系统参数的map中是否存在字段的map,如果不存在,就新建一个
                if (sysParamMap.get(sysParam.getSysParamField()) == null) {
                    fieldMap = new HashMap<>();
                    /**
                     * key  = 1
                     * value = 一级供应商
                     */
                    fieldMap.put(sysParam.getSysParamValue(), sysParam.getSysParamText());
                    /**
                     * key = supType
                     * value = 存储具体数据的Map集合
                     */
                    sysParamMap.put(sysParam.getSysParamField(), fieldMap);
                } else {
                    //如果存在,那么就在原先的Map集合中添加
                    fieldMap = (Map<String, Object>) sysParamMap.get(sysParam.getSysParamField());
                    fieldMap.put(sysParam.getSysParamValue(), sysParam.getSysParamText());
                }
            }
        }
        /**
         * key = shId        value = Map-->(1  主仓库)
         *                             (2  分仓库)
         *
         * key = supType    value= Map-->( 1   一级供应商)
         *                              ( 2  二级供应商)...
         * key = goodsColor  value = Map--> (1  红色)....
         */
        return sysParamMap;
    }
}

controller使用@PostConstruct注解,在初始化的时候就把数据加载到application域对象中

//系统启动时加载参数
    @PostConstruct
    public void initSysParam(){
        loadSysParam();
    }
    //用来加载系统参数  
    public void loadSysParam(){
        Map<String, Object> sysParamMap = sysParamService.selectList();
        application.setAttribute("sysParam", sysParamMap);
        System.out.println("===================系统参数加载成功2=====================");
    }

采购商品

微信图片_20220411225056.jpg

采购表buy_order:

单号bo_id,供货商sup_id,仓库sh_id,收货日期bo_date,应付(实付+欠款+优惠)bo_payable,实付bo_paid,欠款bo_Arrears,原始单号bo_original_id,备注bo_remark,经办人bo_attn,操作员operator。

采购明细表buy_order_detail:

编号bod_id:商品名称goods_id,单位goods_unit,数量 bod_amount,进价bod_buy_price,总金额(可无)bod_total_price,    采购单号bo_id,手机串号列表(##分隔)bod_IMEI

账务表account_records:

编号ad_id,供货商编号sup_id,日期ad_date,单号(不同类型单号不一样)ad_order_id,类型(业务类型)ad_bus_type,应付ad_payable,
    实付ad_paid,欠款ad_arrears,优惠金额ad_discount,经办人ad_attn,操作员ad_operator。备注ad_remark

微信图片_20220411225342.jpg

数据在插入的时候涉及到了这三张的数据库表:

微信图片_20220411225100.jpg

至于我们的财务表的数据是用于拓展的,属性的信息基本是在采购表中获取:

public int insertBuyOrder(BuyOrder buyOrder) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("service.buyOrder:"+buyOrder);
        int i = 0;      
        //生成采购单号,bo表示采购业务
        //bo --商品采购
        //ro --商品退货
        //
        String boId ="bo"+UUID.randomUUID().toString().replace("-", "");
        System.out.println("boIduuid:"+boId);
        //设置采购单号
        buyOrder.setBoId(boId);     
        i = buyOrderMapper.insert(buyOrder);
        //设置采购明细主键及与采购单的外键值
        for(BuyOrderDetail bod : buyOrder.getBuyOrderDetails()){
            bod.setBodId(UUID.randomUUID().toString().replace("-", ""));
            bod.setBoId(boId);
        }
        buyOrderDetailMapper.insertList(buyOrder.getBuyOrderDetails());
        AccountRecords accountRecords = new AccountRecords();
        // 生成并设置怅务记录的主键
        accountRecords.setArId(String.valueOf("ar"+UUID.randomUUID().toString().replace("-", "")));
        accountRecords.setArAttn(buyOrder.getBoAttn());
        accountRecords.setArArrears(buyOrder.getBoArrears());
        //bo表示商品采购,可以在参数表中加入相关内容
        accountRecords.setArBusType("bo");
        accountRecords.setArDate(buyOrder.getBoDate());
        //优惠金额:用应付金额减去实付金额再减去欠款
        accountRecords.setArDiscount(buyOrder.getBoPayable().subtract(buyOrder.getBoPaid()).subtract(buyOrder.getBoArrears()));
        accountRecords.setArOperator(buyOrder.getBoOperator());
        //采购单号
        accountRecords.setArOrderId(buyOrder.getBoId());
        accountRecords.setArPaid(buyOrder.getBoPaid());
        accountRecords.setArPayable(buyOrder.getBoPayable());
        accountRecords.setArRemark(buyOrder.getBoRemark());
        accountRecords.setSupId(buyOrder.getSupId());
        accountRecordsMapper.insert(accountRecords);
        return i;
    }

总结

  • Controller可以使用一个方法来统一进行页面跳转
  • 分页对象的设计可以加上条件,类型为泛型
  • 抽取Service层时,可以使用@PostConstruct//在构造方法后,初化前执行来进行初始化Dao对象。
目录
相关文章
|
1月前
|
Java 应用服务中间件 数据库连接
ssm项目整合,简单的用户管理系统
文章介绍了一个使用SSM框架(Spring、SpringMVC、MyBatis)构建的简单用户管理系统的整合过程,包括项目搭建、数据库配置、各层代码实现以及视图展示。
ssm项目整合,简单的用户管理系统
|
15天前
|
前端开发 Java 关系型数据库
【保姆级SSM教程】高并发朋友圈点赞项目设计
【保姆级SSM教程】高并发朋友圈点赞项目设计
23 0
|
15天前
|
SQL Java 数据库连接
快速搭建SSM项目【最全教程】~令狐小哥版
快速搭建SSM项目【最全教程】~令狐小哥版
18 0
|
2月前
|
Java 数据库连接 Maven
SSM框架整合图书管理项目
这篇文章是关于SSM框架整合到图书管理项目的详细教程,涵盖了从Maven项目构建、依赖导入、数据库连接、配置文件编写、实体类和接口实现到SpringMVC整合的完整步骤。
SSM框架整合图书管理项目
|
2月前
|
Java 应用服务中间件 Maven
Mac使用Idea配置传统SSM项目(非maven项目)
Mac使用Idea配置传统SSM项目(非maven项目)
46 1
WXM
|
3月前
|
Java 应用服务中间件 网络安全
Eclipse运行SSM/SSH项目教程
Eclipse运行SSM/SSH项目教程
WXM
110 0
|
4月前
|
前端开发 Java
基于SSM框架的手机商城项目
基于SSM框架的手机商城项目
47 0
|
4月前
|
前端开发 JavaScript Java
杨校老师项目之基于SSM大学生创新创业项目管理系统
杨校老师项目之基于SSM大学生创新创业项目管理系统
74 0
|
4月前
|
前端开发 Java 关系型数据库
杨校老师项目之基于SSM企业物流快递配送管理系统
杨校老师项目之基于SSM企业物流快递配送管理系统
47 0
|
4月前
|
前端开发 Java 关系型数据库
杨校老师项目之基于SSM社区疫情防控人员访客登记报备平台
杨校老师项目之基于SSM社区疫情防控人员访客登记报备平台
32 0