【JavaEE】电商秒杀项目·第4章·商品模型

一、领域模型先行的思想

上来第一步不是根据产品经理给的需求建数据库、表。
而是先进行领域模型设计,再建库、表。
比如,先想好,密码和用户信息应该分开存放,然后再去建表。
也就是说,先思考设计Model,再去动手建表。

二、Mybatis进行ORM

两个连接数据库的地方

  1. 在Mybatis配置文件中,为映射数据库表做准备
  2. 在SpringBoot项目配置文件中,为CURD操作做准备

Mybatis映射完成后,要检查一遍,DO中属性的数据类型是否和数据库中类型一致

还需要手动在mapper.xml文件的insert方法和insertSelective方法后添加id主键自增的配置

三、服务实现

根据Model设计Service接口,再进行实现

  1. 首先类级别加@Service;createItem方法加@Transactional

  2. 方法体内首先要进行入参校验;

    入参校验第一步是在Model中加上注解;

    然后在ServiceImpl中@AutoWired自动引入ValidatorImpl对象;

    接着就是调用validator对象的validate方法对Model进行校验;

    如果异常,则抛出

  3. 然后就是model转dataobject

    首先判断Model是否为空,如果为空,直接返回null,

    然后new一个ItemDO对象,

    使用BeanUtils.copyProperties(source, target);进行copy

    注意,Model中price是BigDecimal类型,而DO中price是Double类型

    这样会导致copy忽略price

    所以,copy之后,还需要手动转换price的类型,

    再手动set到dataobject中

    数据库中没有BigDecimal,所以只能使用Double

    如果Service也用Double的话 ,有肯能在返回给Controller,Controller再JSON序列化返回给前端时,导致精度丢失,造成死锁。

    如果是BigDecimal的话,返回的时候精度是确定的,不会丢失。

    还有一个需要转化的就是ItemStockDO

    前两步一样,判空,new

    第三步不能使用copy了,因为那样会让id错误

    只能使用set方法

  4. 将dataobject写入数据库

    因为要操作数据库,所以要

    @AutoWired一个itemDOMapper对象

    @AutoWired一个ItemStockDOMapper对象

    用它俩来执行数据库的insertSelective方法

    插入成功后,再从数据库里读出来,返回给调用的方法

    这样,方法在调用的时候,如果发现返回值为null

    这说明插入数据库的过程有问题

  5. getItemById的实现

    这其实属于第3步的返回值,Service从Controller接收Model

    然后插入数据库后,再从数据库中取出来,返回给Controller

    这样,就可以真的确定,Model入库了。

    实现原理就是根据传入参数id,先去数据库中查出来ItemDO

    再根据ItemDO在数据库中的id,即item_stock的外键,来查出来ItemStockDO

    (其实传入参数id就是itemDO在数据库中的id,不知道老师为什么不那样用)

    (那样用确实存在风险,但是我不明白风险什么时候能发生,先记录于此)

    最后再将两个DO整合为Model,返回

    这里也要注意price类型转换的问题

四、Controller实现

  1. 创建一个ItemVO

    这里的ItemVO和ItemModel完全一致,但是老师有强调到,有些情况,VO层会扩增,所以分层是必须的。

    这里还要强调,VO必须有getter、setter

    否则会创建失败

    原因正在思考中…

  2. 创建一个ItemController,依然继承与BaseController

    然后在类级别上加上@Controller声明、RequestMapping映射、@CrossOrigin跨域

    老师在这里强调,我们所做的分层,就是尽可能让Controller层简单,让service层复制,把业务逻辑都尽可能的聚合在Service中

  3. 实现Controller层的createItem方法

    先加上@RequestMapping映射,和@ResponseBody

    声明CommonReturnType类型的方法createItem以及参数映射

    老师在这里接收的参数是具体的类型
    我还是主张所以参数先用String类型接收
    接收到后再转为对应的类型,这样前端传递的容易一些

    方法体就是 封装参数成Model,调用Service,返回创建的商品Model

    把返回的Model转为VO,通过CommonReturnType.create(itemVO);

    返回给前端

  4. convertVOFromModel的实现

    接收service层返回的Model

    判空

    new VO对象

    BeanUtils复制

    返回VO

  5. Controller层getItemList方法实现

    先加上@RequestMapping映射,和@ResponseBody

    声明CommonReturnType类型的方法getItemList以及参数映射

    老师在这里接收的参数是具体的类型
    我还是主张所以参数先用String类型接收
    接收到后再转为对应的类型,这样前端传递的容易一些

    方法体就是接收id,ItemService从数据库里查出Model

    再将Model转为VO

    CommonReturnType进行返回

五、CreateItem前端实现

注意URL请求地址一定要正确即可

六、listItem在DAO层实现

  1. ItemDOMapper.xml新增查询
<select id="listItem"  resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from item order by sales desc;
</select>

上面的语句相当于 SELECT * FROM item ORDER BY sales DESC;

  1. ItemDOMapper.java新增映射方法
List<ItemDO> listItem();

七、listItem在Service层实现

  1. ItemService.java声明接口方法
  2. ItemServiceImpl.java实现接口方法
@Override
public List<ItemModel> listItem() {
    List<ItemDO> itemDOList = itemDOMapper.listItem();
    // Java8 的 stream API map、collect、Collection.toList()
    List<ItemModel> itemModelList = itemDOList.stream().map(
        itemDO -> {
            ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
            ItemModel itemModel = this.convertModelFromDataObject(itemDO, itemStockDO);
            return itemModel;
        }
    ).collect(Collectors.toList());
    return itemModelList;
}

八、listItem在Controller层实现

@RequestMapping(value = "list", method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType listItem(){
    List<ItemModel> itemModelList = itemService.listItem();
    // 使用Java8 stream API将list内的itemModel转为itemVO
    List<ItemVO> itemVOList = itemModelList.stream().map(
        itemModel -> {
            ItemVO itemVO = this.convertVOFromModel(itemModel);
            return itemVO;
        }
    ).collect(Collectors.toList());
    return CommonReturnType.create(itemVOList);
}

九、listItem Android端实现

布局

ConstraintLayout 内嵌 RecyclerView, 实现可回收滚动列表视图

CardView 内嵌 LinearLayout,inflate Adapter,进而recyclerView.setAdapter(itemVOAdapter);实现卡片式布局。

逻辑
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_list_item);
    // 查找控件
    RecyclerView recyclerView = findViewById(R.id.rv_item_list);
    // 设置布局管理器
    GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
    recyclerView.setLayoutManager(layoutManager);
    // 设置数据源
    itemVOList = new ArrayList<>();
    itemVOAdapter = new ItemVOAdapter(this, itemVOList);
    // 设置适配器
    recyclerView.setAdapter(itemVOAdapter);
    // 加载网络数据
    loadUrlData(URL);
    // 下拉刷新
    swipeRefresh = findViewById(R.id.sr_item_list);
    swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
    swipeRefresh.setOnRefreshListener(
            new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    refreshItemList();
                    itemVOAdapter.notifyDataSetChanged();
                    swipeRefresh.setRefreshing(false);
                }
            }
    );

}

下拉刷新的实现,OnRefreshListener()内部方法的实现

// 下拉刷新的实现
private void refreshItemList() {
    new Thread(
            new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            loadUrlData(URL);
                            itemVOAdapter.notifyDataSetChanged();
                            swipeRefresh.setRefreshing(false);
                            Log.d(TAG, "刷新完成!");
                        }
                    });
                }
            }).start();
}

加载网络数据的实现,重点是需要用到AsyncTask<Void, Void, String>(){ … }

// 加载网络数据的实现
@SuppressLint("StaticFieldLeak")
private void loadUrlData(final String url) {
    new AsyncTask<Void, Void, String>() {
        @Override
        protected String doInBackground(Void... params) {
            // 执行网络请求
            String responseBodyStr = getResponseBodyStrByokhttp(url);
            return responseBodyStr;
        }

        @Override
        protected void onPostExecute(String responseBodyStr) {
            if ( !TextUtils.isEmpty(responseBodyStr) ) {
                // 使用前先清除数据
                itemVOList.clear();
                // 解析数据
                // 这里不能使用赋值语法,必须使用AddAll,否则无法显示数据
                itemVOList.addAll(parseJSONData(responseBodyStr));
                // 打乱数据
                Collections.shuffle(itemVOList);
                // 提示adapter更新数据
                itemVOAdapter.notifyDataSetChanged();
            }
        }
    }.execute();
}

Gson解析JSON的实现,重点是强大的Gson可以快速将JsonArray转为List

// okhttp获取ResponseBodyStr
private String getResponseBodyStrByokhttp(String path) {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder().get().url(path).build();
    try {
        Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            ResponseBody body = response.body();
            return body.string();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
// 解析JSON数据的实现
private List<ItemVO> parseJSONData(String responseBodyStr) {
    JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseBodyStr);
    // 响应成功
    if (getStatus(responseBodyJSONObject).equals("success")) {
        // JSON解析为List
        List<ItemVO> jsonToList = parseJSONToList(responseBodyJSONObject);
        Log.d(TAG, "响应success");
        return jsonToList;
    } else {
        getResponseErrMsg(ListItemActivity.this, responseBodyJSONObject);
        Log.d(TAG, "响应fail,获取商品信息失败");
    }
    return null;
}

// 获取响应状态
private String getStatus(JsonObject responseBodyJSONObject) {
    String status = responseBodyJSONObject.get("status").getAsString();
    return status;
}

// 注意这里的返回值不在是单个JsonObject,而是JsonArray
// 强大的Gson可以快速将JsonArray转为List
private List<ItemVO> parseJSONToList(JsonObject responseBodyJSONObject) {
    JsonArray jsonArray = responseBodyJSONObject.get("data").getAsJsonArray();
    Log.d(TAG, "size: " + jsonArray.size());
    Type listType = new TypeToken<List<ItemVO>>() {
    }.getType();
    List<ItemVO> list = new Gson().fromJson(jsonArray, listType);
    return list;
}

在AsyncTask<Void, Void, String>(){ … }中的doInBackground()获取到itemVOList之后,就会执行AsyncTask<Void, Void, String>(){ … }中的onPostExecute(),下面的代码,句句经典!

@Override
protected void onPostExecute(String responseBodyStr) {
    if ( !TextUtils.isEmpty(responseBodyStr) ) {
        // 使用前先清除数据
        itemVOList.clear();
        // 解析数据
        // 这里不能使用赋值语法,必须使用AddAll,否则无法显示数据
        itemVOList.addAll(parseJSONData(responseBodyStr));
        // 打乱数据
        Collections.shuffle(itemVOList);
        // 提示adapter更新数据
        itemVOAdapter.notifyDataSetChanged();
    }
}

这样,就实现了,获取网络数据,通知适配器数据改变,RecyclerView进行刷新,一系列操作。

十、商品详情页

详情页没有整的像淘宝那样炫酷,而是和注册页相似。

IDEA Tips

  1. Use the Ctrl+Alt+Shift+U keyboard shortcut to open a UML class diagram in a new editor.
  2. Use the Ctrl+Alt+U keyboard shortcut to open a UML class diagram in a popup window.
  3. You can open the database console by clicking or pressing .
  4. The database console lets you compose and execute SQL statements (Ctrl+Enter), and also analyze the query results.
  5. You can execute injected SQL statements and the statements in SQL files by pressing Ctrl+Enter. (Alternatively, Alt+Enter | Run Query in Console.)
  6. The Table Editor provides a GUI for working with table data. In the Database tool window, select the table of interest and press F12.
  7. The Database tool window provides access to basic info about the database objects (Ctrl+Shift+空格).

Bug收集

由login_db换为miaosha数据库

导致mybatis无法映射而出错

[外链图片转存失败(img-0DKydYtN-1565141994899)(C:\Users\acer\Documents\Markdown文档\Image\1560163327972.png)]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值