二、搭建zookeeper服務(wù)注冊中心

一、關(guān)于ZooKeeper和Dubbo

1. ZooKeeper(動物園管理員)是什么

注冊中心負(fù)責(zé)服務(wù)地址的注冊與查找,相當(dāng)于目錄服務(wù),服務(wù)提供者和消費(fèi)者只在啟動時與注冊中心交互,注冊中心不轉(zhuǎn)發(fā)請求,壓力較小。使用dubbo-2.3.3以上版本,建議使用zookeeper注冊中心。
ZooKeeper是一個分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是Google的Chubby一個開源的實現(xiàn),它是集群的管理者,監(jiān)視著集群中各個節(jié)點(diǎn)的狀態(tài)根據(jù)節(jié)點(diǎn)提交的反饋進(jìn)行下一步合理操作。最終,將簡單易用的接口和性能高效、功能穩(wěn)定的系統(tǒng)提供給用戶。

2. Dubbo

這個項目是基于SOA架構(gòu)的,表現(xiàn)層和服務(wù)層在不同的系統(tǒng)中,而且還會調(diào)用一些其他系統(tǒng)的服務(wù),所以系統(tǒng)之間的通信時不可避免的。
那么如何進(jìn)行跨系統(tǒng)通信呢?
1、Webservice:效率不高基于soap協(xié)議。項目中不推薦使用。
2、使用restful形式的服務(wù):http+json。很多項目中應(yīng)用。如果服務(wù)太多,服務(wù)之間調(diào)用關(guān)系混亂,需要治療服務(wù)。
3、使用dubbo。使用rpc協(xié)議進(jìn)行遠(yuǎn)程調(diào)用,直接使用socket通信。傳輸效率高,并且可以統(tǒng)計出系統(tǒng)之間的調(diào)用關(guān)系、調(diào)用次數(shù)。

什么是dubbo?

隨著互聯(lián)網(wǎng)的發(fā)展,網(wǎng)站應(yīng)用的規(guī)模不斷擴(kuò)大,常規(guī)的垂直應(yīng)用架構(gòu)已無法應(yīng)對,分布式服務(wù)架構(gòu)以及流動計算架構(gòu)勢在必行,亟需一個治理系統(tǒng)確保架構(gòu)有條不紊的演進(jìn)。

系統(tǒng)架構(gòu)演進(jìn)趨勢.jpg
  • 單一應(yīng)用架構(gòu)
    當(dāng)網(wǎng)站流量很小時,只需一個應(yīng)用,將所有功能都部署在一起,以減少部署節(jié)點(diǎn)和成本。
    此時,用于簡化增刪改查工作量的 數(shù)據(jù)訪問框架(ORM) 是關(guān)鍵。

  • 垂直應(yīng)用架構(gòu)
    當(dāng)訪問量逐漸增大,單一應(yīng)用增加機(jī)器帶來的加速度越來越小,將應(yīng)用拆成互不相干的幾個應(yīng)用,以提升效率。
    此時,用于加速前端頁面開發(fā)的Web框架(MVC)是關(guān)鍵。

  • 分布式服務(wù)架構(gòu)
    當(dāng)垂直應(yīng)用越來越多,應(yīng)用之間交互不可避免,將核心業(yè)務(wù)抽取出來,作為獨(dú)立的服務(wù),逐漸形成穩(wěn)定的服務(wù)中心,使前端應(yīng)用能更快速的響應(yīng)多變的市場需求。
    此時,用于提高業(yè)務(wù)復(fù)用及整合的 分布式服務(wù)框架(RPC) 是關(guān)鍵。

  • 流動計算架構(gòu)
    當(dāng)服務(wù)越來越多,容量的評估,小服務(wù)資源的浪費(fèi)等問題逐漸顯現(xiàn),此時需增加一個調(diào)度中心基于訪問壓力實時管理集群容量,提高集群利用率。
    此時,用于提高機(jī)器利用率的 資源調(diào)度和治理中心(SOA) 是關(guān)鍵。

Dubbo就是資源調(diào)度和治理中心的管理工具。
其核心部分包含:

  1. 遠(yuǎn)程通訊: 提供對多種基于長連接的NIO框架抽象封裝,包括多種線程模型,序列化,以及“請求-響應(yīng)”模式的信息交換方式。
  2. 集群容錯: 提供基于接口方法的透明遠(yuǎn)程過程調(diào)用,包括多協(xié)議支持,以及軟負(fù)載均衡,失敗容錯,地址路由,動態(tài)配置等集群支持。
  3. 自動發(fā)現(xiàn): 基于注冊中心目錄服務(wù),使服務(wù)消費(fèi)方能動態(tài)的查找服務(wù)提供方,使地址透明,使服務(wù)提供方可以平滑增加或減少機(jī)器。

Dubbo的架構(gòu)

Dubbo的架構(gòu).jpg

節(jié)點(diǎn)角色說明:
Provider: 暴露服務(wù)的服務(wù)提供方。
Consumer: 調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方。
Registry: 服務(wù)注冊與發(fā)現(xiàn)的注冊中心。
Monitor: 統(tǒng)計服務(wù)的調(diào)用次調(diào)和調(diào)用時間的監(jiān)控中心。
Container: 服務(wù)運(yùn)行容器。
調(diào)用關(guān)系說明:

  1. 服務(wù)容器負(fù)責(zé)啟動,加載,運(yùn)行服務(wù)提供者。
  2. 服務(wù)提供者在啟動時,向注冊中心注冊自己提供的服務(wù)。
  3. 服務(wù)消費(fèi)者在啟動時,向注冊中心訂閱自己所需的服務(wù)。
  4. 注冊中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。
  5. 服務(wù)消費(fèi)者,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺調(diào)用。
  6. 服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計調(diào)用次數(shù)和調(diào)用時間,定時每分鐘發(fā)送一次統(tǒng)計數(shù)據(jù)到監(jiān)控中心。

dubbo使用方法
Dubbo采用全Spring配置方式,透明化接入應(yīng)用,對應(yīng)用沒有任何API侵入,只需用Spring加載Dubbo的配置即可,Dubbo基于Spring的Schema擴(kuò)展進(jìn)行加載。

發(fā)布服務(wù):

<!-- 和本地服務(wù)一樣實現(xiàn)遠(yuǎn)程服務(wù) -->
<bean id="xxxService" class="com.xxx.XxxServiceImpl" />
<!-- 增加暴露遠(yuǎn)程服務(wù)配置 -->
<dubbo:service interface="com.xxx.XxxService" ref="xxxService" />

調(diào)用服務(wù):

<!-- 增加引用遠(yuǎn)程服務(wù)配置 -->
<dubbo:reference id="xxxService" interface="com.xxx.XxxService" />
<!-- 和本地服務(wù)一樣使用遠(yuǎn)程服務(wù) -->
<bean id="xxxAction" class="com.xxx.XxxAction">
    <property name="xxxService" ref="xxxService" />
</bean>

二、搭建zookeeper服務(wù)注冊中心

1. zookeeper單機(jī)版

第一步:安裝jdk

  1. 最好先建一個專門存放上傳文件的文件夾:
    命令 mkdir -p /app/tools

  2. 上傳jdk的Linux版本到這個文件夾中,最好是1.7版本,和zookeeper比較搭,上次我用了1.8版本,有一些莫名其妙的bug。

    上傳命令:我沒用命令,直接工具上傳的。
    上傳界面
  3. 再新建一個文件夾,存放解壓后的文件,然后解壓到這個文件夾。
    mkdir /usr/local/java
    tar -zxvf jdk-1.7-40.tar.gz -C /usr/local/java/

  4. 配置Linux的jdk環(huán)境變量
    修改/etc/profile文件:vi /etc/profile
    在尾部追加:

# java environment
export JAVA_HOME=/usr/java/jdk1.7.0_40 
export CLASSPATH=.:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar
export PATH=$PATH:${JAVA_HOME}/bin

:wq保存退出

配置Linux的jdk環(huán)境變量
  1. 配置完成后刷新配置: source /etc/profile

  2. 測試是否成功: java -version
    image.png

有可能出現(xiàn)的bug :
-bash: /usr/jdk1.7.0_40/bin/java: /lib/ld-linux.so.2: bad ELF interpreter: 沒有那個文件或目錄
解決方式:
輸入命令 yum install ld-linux.so.2

第二步:解壓zookeeper安裝包
創(chuàng)建zookeeper解壓目錄:mkdir /usr/local/zookeeper
解壓:tar -zxvf zookeeper-3.4.8.tar.gz -C /usr/local/zookeeper/

第三步:將conf文件夾下zoo_sample.cfg復(fù)制一份,改名為zoo.cfg
命令:cp zoo_sample.cfg zoo.cfg

第四步:修改配置dataDir屬性,指定一個真實目錄
在zookeeper根目錄中,創(chuàng)建一個data文件夾和logs文件夾,然后修改conf文件夾中的zoo.cfg的dataDir屬性和dataLogDir屬性:
dataDir=/usr/local/zookeeper/zookeeper-3.4.8/data
dataLogDir=/usr/local/zookeeper/zookeeper-3.4.8/logs

zoo.cfg

第五步:啟動zookeeper并進(jìn)行測試
關(guān)閉防火墻:systemctl stop firewalld
禁止防火墻開機(jī)啟動:systemctl disable firewalld
啟動zookeeper:./zookeeper-3.4.8/bin/zkServer.sh start
查看狀態(tài):./zookeeper-3.4.8/bin/zkServer.sh status
客戶端連接測試以下::./zookeeper-3.4.8/bin/zkCli.sh -server 127.0.0.1:2181

成功狀態(tài)

進(jìn)去看一下就行了,一般也不會動它。

第六步,部署dubbo監(jiān)控中心
首先上傳tomcat,解壓tomcat;
上傳dubbo-admin-2.5.4.war包,放到tomcat的webapps文件夾下面;
啟動tomcat:./startup.sh start
訪問http://192.168.245.130:8080/dubbo-admin-2.5.4/,輸入用戶名root和密碼root,進(jìn)入監(jiān)控頁面,沒有服務(wù)的話,看不出啥效果,我這個已經(jīng)有服務(wù)了。

服務(wù)提供者:
提供者

服務(wù)消費(fèi)者:
消費(fèi)者

2.zookeeper注冊中心集群版

單機(jī)模式就不廢話太多了,開始集群。

第一步,將解壓好的zookeeper文件夾復(fù)制一份:cp -rf zookeeper-3.4.8/ zk1

第二步,修改zk1中的zoo.cfg文件,并在data文件夾下創(chuàng)建myid文件,寫入數(shù)字1

進(jìn)入data文件夾,創(chuàng)建myid:echo 1>>myid

修改zoo.cfg:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/zk1/data
clientPort=2181
dataLogDir=/usr/local/zookeeper/zk1/logs

server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883

這里需要注意,三個端口號是不同的,千萬不要有重復(fù),到時候leader選不出來。

  • 3個端口的作用:
    2181:對cline端提供服務(wù)
    2881:集群內(nèi)機(jī)器通訊使用(Leader監(jiān)聽此端口)
    3881:選舉leader使用

第三步,復(fù)制兩份zk1,分別為zk2、zk3,修改zk2(2182,2)和zk3(2183,3)的zoo.cfg文件和myid文件。

第四步,啟動zookeeper集群,查看狀態(tài)。
./zk1/bin/zkServer.sh start

集群狀態(tài)

為什么是zookeeper集群的基數(shù)三個呢?準(zhǔn)確的說為什么是單數(shù)呢?

  1. 容錯
    所謂的zookeeper容錯是指,當(dāng)宕掉幾個zookeeper服務(wù)器之后,剩下的個數(shù)必須大于宕掉的個數(shù),也就是剩下的服務(wù)數(shù)必須大于n/2,zookeeper才可以繼續(xù)使用,無論奇偶數(shù)都可以選舉leader。5臺機(jī)器最多宕掉2臺,還可以繼續(xù)使用,因為剩下3臺大于5/2。
    說為什么最好為奇數(shù)個,是在以最大容錯服務(wù)器個數(shù)的條件下,會節(jié)省資源,比如,最大容錯為2的情況下,對應(yīng)的zookeeper服務(wù)數(shù),奇數(shù)為5,而偶數(shù)為6,也就是6個zookeeper服務(wù)的情況下最多能宕掉2個服務(wù),所以從節(jié)約資源的角度看,沒必要部署6(偶數(shù))個zookeeper服務(wù)。

  2. 防腦裂

一個zookeeper集群中,可以有多個follower、observer服務(wù)器,但是必需只能有一個leader服務(wù)器。

如果leader服務(wù)器掛掉了,剩下的服務(wù)器集群會通過半數(shù)以上投票選出一個新的leader服務(wù)器。

集群互不通訊情況:

一個集群3臺服務(wù)器,全部運(yùn)行正常,但是其中1臺裂開了,和另外2臺無法通訊。3臺機(jī)器里面2臺正常運(yùn)行過半票可以選出一個leader。

一個集群4臺服務(wù)器,全部運(yùn)行正常,但是其中2臺裂開了,和另外2臺無法通訊。4臺機(jī)器里面2臺正常工作沒有過半票以上達(dá)到3,無法選出leader正常運(yùn)行。

一個集群5臺服務(wù)器,全部運(yùn)行正常,但是其中2臺裂開了,和另外3臺無法通訊。5臺機(jī)器里面3臺正常運(yùn)行過半票可以選出一個leader。

一個集群6臺服務(wù)器,全部運(yùn)行正常,但是其中3臺裂開了,和另外3臺無法通訊。6臺機(jī)器里面3臺正常工作沒有過半票以上達(dá)到4,無法選出leader正常運(yùn)行。

四、服務(wù)的注冊和獲取

1. 先不管用什么服務(wù)測試,我們先使用mybatis-generator工具類自動生成pojo類和mapper類

這是mybatis-generator工具類的項目結(jié)構(gòu),與這個項目無關(guān)。


image.png

需要的jar包圖中有。

GeneratorSqlmap類:

package generator;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class GeneratorSqlmap {

    public void generator() throws Exception{

        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //指定 逆向工程配置文件
        File configFile = new File("generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);

    } 
    public static void main(String[] args) throws Exception {
        try {
            GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }

}

generatorConfig.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自動生成的注釋 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--數(shù)據(jù)庫連接的信息:驅(qū)動類、連接地址、用戶名、密碼 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/yysc" userId="root"
            password="root">
        </jdbcConnection>
        <!-- 默認(rèn)false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,為 true時把JDBC DECIMAL 和 
            NUMERIC 類型解析為java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成PO類的位置 -->
        <javaModelGenerator targetPackage="com.yzy.yysc.manager.pojo"
            targetProject="D:\IDEAWorkPlace\yysc\yy-parent\generatorSqlmapCustom\src\main\java">
            <!-- enableSubPackages:是否讓schema作為包的后綴 -->
            <property name="enableSubPackages" value="false" />
            <!-- 從數(shù)據(jù)庫返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.yzy.yysc.manager.mapper"
            targetProject="D:\IDEAWorkPlace\yysc\yy-parent\generatorSqlmapCustom\src\main\java">
            <!-- enableSubPackages:是否讓schema作為包的后綴 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="com.yzy.yysc.manager.mapper"
            targetProject="D:\IDEAWorkPlace\yysc\yy-parent\generatorSqlmapCustom\src\main\java">
            <!-- enableSubPackages:是否讓schema作為包的后綴 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定數(shù)據(jù)庫表 -->
        <table schema="" tableName="tb_content"></table>
        <table schema="" tableName="tb_content_category"></table>
        <table schema="" tableName="tb_item"></table>
        <table schema="" tableName="tb_item_cat"></table>
        <table schema="" tableName="tb_item_desc"></table>
        <table schema="" tableName="tb_item_param"></table>
        <table schema="" tableName="tb_item_param_item"></table>
        <table schema="" tableName="tb_order"></table>
        <table schema="" tableName="tb_order_item"></table>
        <table schema="" tableName="tb_order_shipping"></table>
        <table schema="" tableName="tb_user"></table>

    </context>
</generatorConfiguration>

配置好以后,直接運(yùn)行GeneratorSqlmap類中的main方法就好了,會自動在src\main\java下生成。
碰到的bug說一下:
bug1:一直報一個找不到generatorConfig.xml文件的錯誤。
解決:修改working directory,讓他和Output path相同


working directory
Output path

bug2:運(yùn)行良好卻不生成文件
解決:targetProject的位置改為絕對位置


targetProject

以前也用過,不過用的maven插件,想用插件的可以搜一下用法。
這些都是它自動生成我復(fù)制過來的:


image.png
image.png

2. 就決定測試獲取商品的服務(wù)好了

商品是這個表:


image.png

商品pojo類,因為涉及到跨系統(tǒng),必須實現(xiàn)序列化接口,它完全是根據(jù)數(shù)據(jù)庫表的字段自動生成的。
我畫蛇添足的把外鍵改成了該外鍵對應(yīng)的類,現(xiàn)在一想這是mybatis不是hibernate,沒有必要這樣做,完全可以使用多表聯(lián)查。這里改了那么待會mapper.xml文件中也要改。

TbItem類:

package com.yzy.yysc.manager.pojo;

import com.yzy.yysc.common.vo.PageVo;

import java.io.Serializable;
import java.sql.Timestamp;

public class TbItem extends PageVo implements Serializable{
    private Long id;

    private String title;

    private String sellPoint;

    private Long price;

    private Integer num;

    private String barcode;

    private String image;

   //我改動的地方
    private TbItemCat itemCat;

    private Byte status;

    private Timestamp created;

    private Timestamp updated;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title == null ? null : title.trim();
    }

    public String getSellPoint() {
        return sellPoint;
    }

    public void setSellPoint(String sellPoint) {
        this.sellPoint = sellPoint == null ? null : sellPoint.trim();
    }

    public Long getPrice() {
        return price;
    }

    public void setPrice(Long price) {
        this.price = price;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public String getBarcode() {
        return barcode;
    }

    public void setBarcode(String barcode) {
        this.barcode = barcode == null ? null : barcode.trim();
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image == null ? null : image.trim();
    }

    public TbItemCat getItemCat() {
        return itemCat;
    }

    public void setItemCat(TbItemCat itemCat) {
        this.itemCat = itemCat;
    }

    public Byte getStatus() {
        return status;
    }

    public void setStatus(Byte status) {
        this.status = status;
    }

    public Timestamp getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {

        this.created = created;
    }

    public Timestamp getUpdated() {
        return updated;
    }

    public void setUpdated(Timestamp updated) {
        this.updated = updated;
    }
}

上面的pojo類我繼承了一個PageVo類,這個PageVo類是用來接收前臺的分頁數(shù)據(jù)的,我放在yysc-common項目中:

package com.yzy.yysc.common.vo;


import java.io.Serializable;

public class PageVo implements Serializable{
    //封裝當(dāng)前頁碼
    private Integer page;
    //封裝每頁數(shù)據(jù)量
    private Integer limit;


    public Integer getPage() {
        return page;
    }

    public void setPage(Integer page) {
        this.page = page;
    }

    public Integer getLimit() {
        return limit;
    }

    public void setLimit(Integer limit) {
        this.limit = limit;
    }

}

我又新建了一個yysc-manager-pojo項目中新建了一個TbItemVo類,并讓它繼承了TbItem類,這個類是用來封裝一些前臺查詢條件的,如關(guān)鍵字,日期等,我們與前臺的業(yè)務(wù)交互時,一般使用這個類。

package com.yzy.yysc.manager.vo;

import com.yzy.yysc.manager.pojo.TbItem;
import org.apache.commons.lang3.StringUtils;

import java.sql.Timestamp;

public class TbItemVo extends TbItem {
    private String keywords;
    private Timestamp createTime;
    private Timestamp endTime;

    public String getKeywords() {
        return keywords;
    }

    public void setKeywords(String keywords) {
        this.keywords = keywords;
    }

    public Timestamp getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {

        if (StringUtils.isNotBlank(createTime)) {
            Timestamp ts = new Timestamp(System.currentTimeMillis());
            try {
                ts = Timestamp.valueOf(createTime + " 00:00:00");
                endTime = Timestamp.valueOf(createTime + " 23:59:59");
                System.out.println(ts);

            } catch (Exception e) {
                e.printStackTrace();
            }

            this.createTime = ts;
        }else{
            this.createTime = null;
        }
    }

    public Timestamp getEndTime() {
        return endTime;
    }
}

mybatis-generator還自動生成了一個TbItemExample類,與數(shù)據(jù)庫做curd操作的時候就會使用它。
這個類太長了,就不上代碼了,但是我們后面需要在它里面添加這樣一個方法,方便模糊查詢。

   public Criteria andOrItemsLike(String value){
            value="%"+value+"%";
            addCriterion("(title like \""+value+"\" or sell_point like \""+value+"\")");
            return (Criteria) this;
        }

商品類的mapper接口和mapper.xml文件,都是自動生成的,我沒有添加任何方法,沒有必要,因為與數(shù)據(jù)庫交互都是使用TbItemExample類來完成。
TbItemMapper接口:

package com.yzy.yysc.manager.mapper;

import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.pojo.TbItemExample;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface TbItemMapper{
    int countByExample(TbItemExample example);

    int deleteByExample(TbItemExample example);

    int deleteByPrimaryKey(Long id);

    int insert(TbItem record);

    int insertSelective(TbItem record);

    List<TbItem> selectByExample(TbItemExample example);

    TbItem selectByPrimaryKey(Long id);

    int updateByExampleSelective(@Param("record") TbItem record, @Param("example") TbItemExample example);

    int updateByExample(@Param("record") TbItem record, @Param("example") TbItemExample example);

    int updateByPrimaryKeySelective(TbItem record);

    int updateByPrimaryKey(TbItem record);

}

商品的mapper.xml文件也很長,我就不全上了,只上一個做了改動的地方,最后面。

TbItemMapper.xml:

<mapper namespace="com.yzy.yysc.manager.mapper.TbItemMapper" >
    <resultMap id="BaseResultMap" type="com.yzy.yysc.manager.pojo.TbItem">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="title" property="title" jdbcType="VARCHAR"/>
        <result column="sell_point" property="sellPoint" jdbcType="VARCHAR"/>
        <result column="price" property="price" jdbcType="BIGINT"/>
        <result column="num" property="num" jdbcType="INTEGER"/>
        <result column="barcode" property="barcode" jdbcType="VARCHAR"/>
        <result column="image" property="image" jdbcType="VARCHAR"/>
        <result column="status" property="status" jdbcType="TINYINT"/>
        <result column="created" property="created" jdbcType="TIMESTAMP"/>
        <result column="updated" property="updated" jdbcType="TIMESTAMP"/>
        <association property="itemCat" column="cid" javaType="com.yzy.yysc.manager.pojo.TbItemCat"
                     select="queryCatById"/>
    </resultMap>

    <select id="queryCatById" resultType="com.yzy.yysc.manager.pojo.TbItemCat">
        SELECT * from tb_item_cat where id=#{cid}
    </select>

商品的interface接口

package com.yzy.yysc.manager.service;

import com.yzy.yysc.common.model.DataGirdModel;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.vo.TbItemVo;

public interface ItemService {
  
    public DataGirdModel<TbItemVo> getAllItems(TbItemVo itemVo);

}

這里要打個叉,因為我前段用的layui展示數(shù)據(jù),所以我們還需要建一個DataGirdModel類把數(shù)據(jù)封裝成layui框架可以就收的格式。
我們DataGirdModel類放在yysc-common項目中,記住也要實現(xiàn)序列化接口。

package com.yzy.yysc.common.model;

import java.io.Serializable;
import java.util.List;

public class DataGirdModel<T> implements Serializable {
    private Integer code=0;
    private String msg;
    private Integer count;
    private List<T> data;


    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Integer getCount() {
        return count;
    }


    public void setCount(Integer count) {
        this.count = count;
    }

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

商品的interface接口實現(xiàn)類:

package com.yzy.yysc.manager.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.yzy.yysc.manager.mapper.TbItemMapper;
import com.yzy.yysc.common.model.DataGirdModel;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.pojo.TbItemExample;
import com.yzy.yysc.manager.service.ItemService;
import com.yzy.yysc.manager.vo.TbItemVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ItemServiceImpl implements ItemService {

    @Autowired
    private TbItemMapper itemMapper;

    @Override
    public DataGirdModel<TbItemVo> getAllItems(TbItemVo itemVo) {

      //分頁查詢
        Page page= PageHelper.startPage(itemVo.getPage(),itemVo.getLimit());

        DataGirdModel<TbItemVo> dgm=new DataGirdModel<>();
        TbItemExample example = new TbItemExample();
        TbItemExample.Criteria criteria= example.createCriteria();
        //模糊查詢
        if(StringUtils.isNotBlank(itemVo.getKeywords())){
            criteria.andOrItemsLike(itemVo.getKeywords());
        }
      //日期條件查詢
        if(itemVo.getCreateTime() != null){  
criteria.andCreatedBetween(itemVo.getCreateTime(),itemVo.getEndTime());
        }
        List<TbItem> demos = itemMapper.selectByExample(example);
        List<TbItemVo> tivolist=new ArrayList<>();
        System.out.println(demos);
        for(TbItem item:demos){
            TbItemVo tivo=new TbItemVo();
            BeanUtils.copyProperties(item,tivo);
            tivolist.add(tivo);
        }
        //注意這里,設(shè)置總條數(shù)一定要放在mapper查詢的后面,不然就是0
        dgm.setCount(new Integer((int) page.getTotal()));
        dgm.setData(tivolist);
        return dgm;
    }

}


下面是web層ItemController類

package com.yzy.yysc.manager.controller;

import com.yzy.yysc.common.model.DataGirdModel;
import com.yzy.yysc.manager.pojo.TbItem;
import com.yzy.yysc.manager.pojo.TbItemCat;
import com.yzy.yysc.manager.service.ItemCatService;
import com.yzy.yysc.manager.service.ItemService;
import com.yzy.yysc.manager.vo.TbItemVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("/item")
public class ItemController {

    @Autowired
    private ItemService itemService;
    @Autowired
    private ItemCatService itemCatService;

    @RequestMapping("/list")
    public String list(){
        return "item/list";
    }

    @RequestMapping("/new")
    public String newItem(Model model){

        return "item/add";
    }

    @RequestMapping("/pageData")
    @ResponseBody
    public DataGirdModel<TbItemVo> getItemByKeywords(TbItemVo itemVo){
        System.out.println(itemVo.getKeywords());
        System.out.println(itemService.getAllItems(itemVo).getCount());

        return itemService.getAllItems(itemVo);
    }


}

接下來配置service層和web層的spring配置文件,進(jìn)行服務(wù)的發(fā)布和獲取,測試系統(tǒng)間的通信是否順暢

  • service層發(fā)布服務(wù):
    注意添加文檔約束:
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    http://code.alibabatech.com/schema/dubbo
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    發(fā)布服務(wù):
 <!-- 發(fā)布dubbo服務(wù) -->
    <!-- 提供方應(yīng)用信息,用于計算依賴關(guān)系 -->
    <dubbo:application name="yy-manager" />
    <!-- 注冊中心的地址 -->
    <dubbo:registry protocol="zookeeper" 
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />
    <!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 聲明需要暴露的服務(wù)接口 -->
    <dubbo:service interface="com.yzy.yysc.manager.service.ItemService" 
ref="itemServiceImpl" timeout="300000"/>

service層發(fā)布服務(wù)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
         http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
       ">
        
       <!-- 將業(yè)務(wù)對象,那入spring容器 -->
       <context:component-scan base-package="com.yzy.yysc.manager.service.impl">
       </context:component-scan>
       
       <!-- 聲明事務(wù)管理器對象 -->
       <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 注入dataSource -->
            <property name="dataSource" ref="dataSource"></property>
       </bean>
       
       <!-- 聲明事務(wù)切面 -->
       <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="save*" propagation="REQUIRED" />
                <tx:method name="add*" propagation="REQUIRED" />
                <tx:method name="update*" propagation="REQUIRED" />
                <tx:method name="delete*" propagation="REQUIRED" />
                <tx:method name="get*" propagation="REQUIRED"  read-only="true"/>
                <tx:method name="load*" propagation="REQUIRED"  read-only="true"/>
                <tx:method name="user*" propagation="REQUIRED"/>
                
                <tx:method name="*" propagation="REQUIRED"  read-only="true"/>
                
            </tx:attributes>
       </tx:advice>
       
       
       <!-- 進(jìn)行aop配置  -->
       <aop:config>
            <aop:pointcut expression="execution(* com.yzy.yysc.manager.service.impl.*.*(..))" id="pc"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
       </aop:config>

    <!-- 發(fā)布dubbo服務(wù) -->
    <!-- 提供方應(yīng)用信息,用于計算依賴關(guān)系 -->
    <dubbo:application name="yy-manager" />
    <!-- 注冊中心的地址 -->
    <dubbo:registry protocol="zookeeper" 
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />
    <!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 聲明需要暴露的服務(wù)接口 -->
    <dubbo:service interface="com.yzy.yysc.manager.service.ItemService" 
ref="itemServiceImpl" timeout="300000"/>

</beans>

  • web層獲取服務(wù)
    同樣注意添加文檔約束:
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    http://code.alibabatech.com/schema/dubbo
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    引用服務(wù):
 <!-- 引用dubbo服務(wù) -->
    <dubbo:application name="yy-manager-web"/>
    <dubbo:registry protocol="zookeeper" 
 address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />

    <dubbo:reference 
    interface="com.yzy.yysc.manager.service.ItemService" id="itemService" />
 
image.png
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
       ">
       <context:component-scan base-package="com.yzy.yysc.manager.controller"></context:component-scan>
         
       <!-- 
            springmvc的注解驅(qū)動
        -->
       <mvc:annotation-driven></mvc:annotation-driven>
       
       
      <!--  配置二進(jìn)制流解析器
       <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
          &lt;!&ndash; 設(shè)置允許上傳的文件最大的尺寸 &ndash;&gt;
            <property name="maxUploadSize" value="10240000"></property>
            &lt;!&ndash; 設(shè)置文件上傳過程中,中文的編碼 &ndash;&gt;
            <property name="defaultEncoding" value="UTF-8"></property>
            &lt;!&ndash; 設(shè)置文件上傳的保存目錄 &ndash;&gt;
            <property name="uploadTempDir" value="/upload"></property>
       </bean>-->

    <mvc:resources location="/WEB-INF/js/" mapping="/js/**"/>
    <mvc:resources location="/WEB-INF/css/" mapping="/css/**"/>
    <mvc:resources location="/WEB-INF/images/" mapping="/images/**"/>
    <mvc:resources location="/WEB-INF/zTree/" mapping="/zTree/**"/>
    <mvc:resources location="/WEB-INF/layui/" mapping="/layui/**"/>
    <mvc:resources location="/WEB-INF/jsp/" mapping="/jsp/**"/>

    <!-- 配置視圖解析器-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"></property>
            <property name="suffix" value=".jsp"></property>
      </bean>

    <!-- 引用dubbo服務(wù) -->
    <dubbo:application name="yy-manager-web"/>
    <dubbo:registry protocol="zookeeper" 
address="192.168.245.130:2181,192.168.245.130:2182,192.168.245.130:2183" />

    <dubbo:reference 
   interface="com.yzy.yysc.manager.service.ItemService" id="itemService" />

</beans>

這是前端的結(jié)構(gòu),記住導(dǎo)入layui和zTree樹

image.png

商品類的列表頁面和js文件


image.png

list.jsp

<%--
  Created by IntelliJ IDEA.
  User: yangoctopus
  Date: 2018/10/11
  Time: 21:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>商品管理</title>
    <link rel="stylesheet" href="/layui/css/layui.css">
    <script type="text/javascript" src="/js/jquery.js"></script>
    <script type="text/javascript" src="/js/jquery.form.min.js"></script>
    <script type="text/javascript" src="/layui/layui.js"></script>
    <script type="text/javascript" src="/js/item/itemManager.js"></script>

</head>
<body>
<div class="layui-card">
    <div class="layui-card-body">
        <form class="layui-form" id="queryForm" method="post">
            <div class="layui-form-item">
                <div class="layui-col-md11">
                    <label class="layui-form-label" style="width: auto">模糊查詢</label>
                    <div class="layui-input-inline">
                        <input style="width: 250px" type="text"  name="keywords" id="key" class="layui-input" placeholder="請輸入商品標(biāo)題或賣點(diǎn)">
                    </div>

                    <div class="layui-inline" style="margin-left: 50px">
                        <label class="layui-form-label" style="width: auto">入庫日期</label>
                        <div class="layui-input-inline" style="width: 130px;">
                            <input type="text" name="createTime" id="createTime"  class="layui-input" autocomplete="off">
                        </div>
                    </div>

                    <div class="layui-btn-group">
                        <button type="button" lay-submit class="layui-btn" lay-filter="sub" lay-submit="" id="queryPage" >查詢</button>
                        <button type="button"  class="layui-btn" id="new"><i class="layui-icon">&#xe638;</i>新建</button>
                    </div>

                </div>
            </div>

        </form>
        <div class="contain-img-table"><table id="dataTable" lay-filter="dataTable"></table></div>
    </div>
</div>

</body>
<script type="text/html" id="barDemo">
    <a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">查看</a>
    <a class="layui-btn layui-btn-xs" lay-event="edit">編輯</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">刪除</a>
</script>

</html>

itemManager.js文件

/*
通過加載事件,發(fā)送ajax請求,加載商品列表
*/
$(document).ready(function(){

    layui.use(['laydate', 'table', 'layer', 'form','jquery'], function () {


        var laydate = layui.laydate, table = layui.table, layer = layui.layer, form = layui.form,$= layui.jquery;
        form.render();
        //日期
        laydate.render({
            elem: '#createTime',
            type: 'date'
        });

        var dataTable = table.render({
            elem: '#dataTable',
            url: '/item/pageData', //數(shù)據(jù)接口
            page: true, //開啟分頁
            method:'post',
            cols: [[ //表頭
                {field: 'id', align: 'center',width : 80,  title: '商品id'},
                {field: 'title', align: 'center',width : 120, title: '商品標(biāo)題'},
                {field: 'sellPoint', align: 'center',width : 120, title: '商品賣點(diǎn)'},
                {field: 'price', align: 'center', width : 100,title: '商品價格'},
                {field: 'num', align: 'center',width : 80, title: '庫存'},
                {align: 'center',width : 100, title: '所屬類目',templet:function(d){
                        return d.itemCat.name;
                    }},
                {field: 'status', align: 'center', width : 90,title: '商品狀態(tài)',templet: function (d) {
                        if(d.status===1){
                            return '正常';
                        }else if(d.status===2){
                            return '下架';
                        }else{
                            return '刪除';
                        }

                    }},
                {field: 'created', align: 'center',width : 120, title: '入庫時間',templet:function(d){

                        return createTime(d.created);
                    }},
                {field: 'updated', align: 'center',width : 120, title: '最后更新時間',templet:function(d){

                        return createTime(d.updated);
                    }},
                {fixed: 'right',title: '操作', align: 'center',width : 160, toolbar:'#barDemo'}
            ]]

        });

        window.reloadTable = function() {

            var fields = {};
            $.each($('#queryForm').serializeArray(), function(i, field){
                fields[field.name] = field.value;
            });

            dataTable.reload({
                where: fields
            });

            //$("#detai").click(tableOn())

        }
        window.tableOn = function(){

            table.on('tool(dataTable)', function(obj){ //注:tool是工具條事件名,test是table原始容器的屬性 lay-filter="對應(yīng)的值"
                var data = obj.data; //獲得當(dāng)前行數(shù)據(jù)
                var layEvent = obj.event; //獲得 lay-event 對應(yīng)的值(也可以是表頭的 event 參數(shù)對應(yīng)的值)
                var tr = obj.tr; //獲得當(dāng)前行 tr 的DOM對象

                if(layEvent === 'edit'){ //編輯
                } else if(layEvent === 'del'){ //刪除
                    layer.confirm('真的刪除行么', function(index){
                        $.ajax({
                            url: '/traceMushrootDibgrow/delete',
                            data: {id: data.id},
                            async: false,
                            success: function (data, status, xhr) {
                                if (data.success) {
                                    layer.close(index);
                                }
                                reloadTable();
                            }
                        });
                    });
                } else if(layEvent === 'detail'){ //查看
                    var img = "<div><img src='/images/11.jpg' height='330'/><br/>data.barcode</div>"
                            layer.open({
                                type: 1,
                                title: '商品圖片',
                                offset: ['10px', '230px'],
                                area:['600px', '400px'],
                                content: img //注意,如果str是object,那么需要字符拼接。

                            });

                    //同步更新緩存對應(yīng)的值
                    // obj.update({username: '123',title: 'xxx'});
                }
            });
        }

        $('#new').click(function () {
            $.ajax({
                url: '/item/new',
                async: false,
                success: function (data, status, xhr) {
                    layer.open({
                        type: 1,
                        title:"商品入庫",
                        offset: ['10px', '50px'],
                        area: ['1000px', '500px'],
                        content: data //注意,如果str是object,那么需要字符拼接。

                    });

                }

            });
        });

        $('#queryPage').click(reloadTable);

        // form.render('table','data');
        tableOn()

    });
});

function createTime(v){
    var date = new Date(v);
    var y = date.getFullYear();
    var m = date.getMonth()+1;
    m = m<10?'0'+m:m;
    var d = date.getDate();
    d = d<10?("0"+d):d;
    var h = date.getHours();
    h = h<10?("0"+h):h;
    var M = date.getMinutes();
    M = M<10?("0"+M):M;
    var str = y+"-"+m+"-"+d+" "+h+":"+M;
    return str;
}

最后,順序啟動項目進(jìn)行測試。
一、啟動yysc-manager-service項目,進(jìn)行服務(wù)注冊。
二、啟動yysc-manager-web項目,進(jìn)行服務(wù)獲取。
效果:

初始頁面:
初始頁面

模糊查詢:
模糊查詢

日期查詢:
日期查詢

關(guān)鍵字+日期多條件查詢:
關(guān)鍵字+日期多條件查詢
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容