【RabbitMQ訊息中介軟體】13.RabbitMQ結合SSM框架-與銷售系統互動
阿新 • • 發佈:2019-02-16
接上一篇:https://blog.csdn.net/acmman/article/details/79778241
我們回顧一下之前的最終的資訊互動模式圖:
其中“倉儲系統”用於貨物的入庫和管理,當貨物更新時,將貨物資訊推送至訊息佇列。而“銷售系統”主要負責貨物的銷售,貨物資訊從訊息佇列中同步過來。
一、編寫“銷售系統”
對於“銷售系統”,其整體架構與“倉儲系統”幾乎一致,建立工程和搭建工程參考上一篇搭建“倉儲系統”的程式碼,這裡僅闡述一下“銷售系統”的業務模組。
按照“倉儲系統”的工程架構,將“銷售系統”搭建起來:
更改其中的資料庫篇配置檔案,讓其連線銷售系統的資料庫:
其中建立一個名為commodity的商品表:
建表語句:
然後建立Mapper配置檔案“CommodityMapper.xml”:
然後在“com.sale_system.po”包下建立商品bean物件“Commodity”:
然後Servie與Dao層與“倉儲系統”結構類似,分別是商品的增刪改查。編寫完畢後的java程式碼層結構如下:
請求響應層Controller類:
然後主頁home.jsp:
預設頁面index.jsp的跳轉路徑為"commodity/home.action":
將工程部署至tomcat:
啟動tomcat,在瀏覽器中分別開啟倉儲系統和銷售系統:
目前它們的資料是相互隔離開來的,我們下面使用RabbitMQ來讓兩者進行資料互動,使得銷售系統可以同步倉儲系統的貨物資料。
二、搭建RabbitMQ連線環境
下面分別在倉儲系統和銷售系統中新增RabbitMQ的連線環境,由於配置類似,這裡以“生產者”和“消費者”來區分兩工程的不同配置。
首先我們在pom.xml新增RabbitMQ的相關依賴:
然後在src/main/resource下建立一個名為“beans-rabbitmq.xml”的配置檔案,為rabbitmq的配置檔案:
編寫該檔案:
然後編寫該配置檔案:
(1)首先新增RabbitMQ的連線工廠(生產者和消費者都配置)
內容:
然後在spring配置檔案beans.xml的資料庫配置檔案引入後面,引入該properties配置:
(2)定義交換機(僅生產者配置)
這裡我們定義一個“萬用字元”型別的交換機:
(3)定義模板(僅生產者配置)
下面定義Rabbit模板,該模板會載入獲取與rabbit互動的連線工廠和相關的交換機,在Java應用層可以注入該類進行與RabbitMQ的各種資料互動,十分重要:
(4)定義佇列和監聽(僅消費者配置)
這裡需要為消費者的配置檔案中配置接收資訊的佇列配置:
生產者完整的配置如下:
消費者的配置如下:
最後,在spring的配置檔案beans.xml的最下面引入該xml:
至此RabbitMQ的基本環境搭建完畢。
這裡細心的朋友可能會注意到,我們並沒有在生產者的配置檔案中宣告佇列並且繫結交換機,其實這一步操作不應該在這裡做,因為後臺一旦進行了配置,則會耦合度比較高,當繫結關係發生變化時,需要重啟專案,十分的不友好。所以,繫結關係最好是手動在RabbitMQ的管理工具裡指定。
登入RabbitMQ的管理工具,開啟Exchanges模組:
建立一個名為“JACK-ITEM-EXCHANGE”的交換機:
然後開啟Queues,建立一個名為“JACK-WEB-ITEM-QUEUE”的佇列:
然後回到Exchanges模組,將佇列繫結在交換機上:
三、倉儲系統對外發布訊息
在之前的業務涉及中,我們要求倉儲系統在新增、刪除以及編輯的時候需要將資訊傳輸至訊息佇列,所以我們在相關方法中新增與rabbit互動的程式碼。
首先在Controller類中注入rabbitTemplate:
然後修改貨物新增的Controller方法,在insert之後,我們要與RabbitMQ互動,將新增的資訊推送到交換機:
這裡要做兩個操作,由於之前編寫MyBatis的時候沒有指定自動返回主鍵ID,這裡需要在Mapper配置檔案中的insert配置中新增useGeneratedKeys、keyProperty、keyColumn三個引數:
然後由於我們要封裝和解析json,在兩個工程的POM檔案中都新增fastJson的依賴:
之後在倉儲系統的編輯與刪除Controller想贏方法中同樣新增訊息通知的程式碼:
下面要編寫銷售系統與RabbitMQ的互動,用於獲取倉儲系統同步的資料。
四、銷售系統同步訊息佇列商品資訊
在銷售系統中要時刻保持與倉儲系統中的貨物資訊一致,所以這裡需要編寫一個消費者,用於從RabbitMQ中實時監聽資料變更資訊。
首先建立一個“com.sale_system.mq.handle”包,用於放置與MQ相關的控制器類。然後建立一個名為“ItemMQHandler”的類,用於實現資訊監聽:
然後在銷售系統的mq的xml配置檔案中新增該bean:
然後定義一個監聽器,用來監聽RabbitMQ中指定的佇列:
佇列與交換機已經人工在管理介面中綁定了,這裡不再贅述。
然後我們回到ItemMQHandler類中進行資訊的具體處理:
我們重啟倉庫系統和銷售系統,登入後依然沒有變化:
但是我們可以在控制檯看到,銷售系統正在實時監聽訊息佇列:
此時我們在倉儲系統中插入一條新的資料:
檢視資料庫,也進行了資料儲存:
然後我們修改前三個資料:
此時根據編輯空則更新的規則,發現銷售系統也同步過來了:
為了方便大家學習,我將部落格中編寫的倉儲系統和銷售系統的最終原始碼分享出來,希望能幫助到大家學習:
-----------------------------------華麗的分割線(#^.^#)----------------------------------------------
上次我們編寫了RabbitMQ結合SSM框架的資料提供方“倉儲系統”的工程,下面我們來編寫要從MQ獲取資訊的“銷售系統”,並且實現“倉儲系統”與“銷售系統”的資訊互動。
我們回顧一下之前的最終的資訊互動模式圖:
其中“倉儲系統”用於貨物的入庫和管理,當貨物更新時,將貨物資訊推送至訊息佇列。而“銷售系統”主要負責貨物的銷售,貨物資訊從訊息佇列中同步過來。
一、編寫“銷售系統”
對於“銷售系統”,其整體架構與“倉儲系統”幾乎一致,建立工程和搭建工程參考上一篇搭建“倉儲系統”的程式碼,這裡僅闡述一下“銷售系統”的業務模組。
按照“倉儲系統”的工程架構,將“銷售系統”搭建起來:
更改其中的資料庫篇配置檔案,讓其連線銷售系統的資料庫:
然後使用sqlyog工具,在資料庫中建立一個名為“sale_system”的資料庫:jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/sale_system jdbc.username=root jdbc.password=1234 c3p0.pool.maxPoolSize=400 c3p0.pool.minPoolSize=50 c3p0.pool.initialPoolSize=50 c3p0.pool.acquireIncrement=100
其中建立一個名為commodity的商品表:
建表語句:
CREATE TABLE `commodity` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `cname` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '商品名稱', `cprice` double NOT NULL COMMENT '商品價格', `cdesc` text COLLATE utf8_bin COMMENT '商品描述', `weight` int(11) DEFAULT NULL COMMENT '重量', `model` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '型號規格', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
然後建立Mapper配置檔案“CommodityMapper.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.sale_system.mapper.CommodityMapper">
<!-- resultMap對映 -->
<resultMap type="com.sale_system.po.Commodity" id="commodityRM">
<!-- 主鍵 -->
<id property="id" column="id" jdbcType="INTEGER" />
<!-- 一般屬性 -->
<result property="name" column="cname" jdbcType="VARCHAR"/>
<result property="price" column="cprice" jdbcType="DOUBLE"/>
<result property="desc" column="cdesc"/>
<result property="weight" column="weight" jdbcType="INTEGER"/>
<result property="model" column="model" jdbcType="VARCHAR"/>
</resultMap>
<!-- 查詢一個 -->
<select id="selectById" parameterType="int" resultMap="commodityRM">
select * from commodity
where id=#{id}
</select>
<!-- 查詢 -->
<select id="selectAll" resultMap="commodityRM">
select * from commodity
</select>
<insert id="insert" parameterType="com.sale_system.po.Commodity">
insert into commodity
(<if test="id !=null">ID,</if>CNAME,CPRICE,CDESC,WEIGHT,MODEL)
values
(
<if test="id !=null">
#{id,jdbcType=INTEGER},
</if>
#{name,jdbcType=VARCHAR},
#{price,jdbcType=DOUBLE},
#{desc},
#{weight,jdbcType=INTEGER},
#{model,jdbcType=VARCHAR}
)
</insert>
<!-- 修改語句 -->
<update id="update" parameterType="com.sale_system.po.Commodity">
update commodity
<set>
<if test="name != null">cname=#{name},</if>
<if test="price != null">cprice=#{price},</if>
<if test="desc != null">cdesc = #{desc},</if>
<if test="weight != null">weight=#{weight},</if>
<if test="model != null">model=#{model}</if>
</set>
where id=#{id}
</update>
<!-- 刪除一條 -->
<delete id="deleteById" parameterType="int">
delete from commodity
where id=#{id}
</delete>
</mapper>
要注意的是,insert配置中本不應該插入id的,因為id是自增的,無需插入,但是這裡我們的資料在同步的時候是需要同步相關ID的,所以當ID存在的時候,需要插入。然後在“com.sale_system.po”包下建立商品bean物件“Commodity”:
package com.sale_system.po;
public class Commodity {
private int id;
private String name;
private Double price;
private String desc;
private Integer weight;
private String model;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
然後Servie與Dao層與“倉儲系統”結構類似,分別是商品的增刪改查。編寫完畢後的java程式碼層結構如下:
請求響應層Controller類:
package com.sale_system.controller;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.sale_system.po.Commodity;
import com.sale_system.service.CommodityService;
@Controller
public class CommdityController {
@Autowired
private CommodityService commodityService;
Log log = LogFactory.getLog(this.getClass());
@RequestMapping("/commodity/home.action")
public String list(Model model){
List<Commodity> commodityList = commodityService.selectAll(null);
model.addAttribute("commodityList",commodityList);
return "/commodity/home.jsp";
}
@RequestMapping("/commodity/toAdd.action")
public String toAdd(Model model){
return "/commodity/add.jsp";
}
@RequestMapping("/commodity/add.action")
public String add(Model model,Commodity commodity){
commodityService.insert(commodity);
return list(model);
}
@RequestMapping("/commodity/toEdit.action")
public String toEdit(Model model,Integer id){
if(id!=null){
model.addAttribute("commodity", commodityService.selectById(id));
}
return "/commodity/edit.jsp";
}
@RequestMapping("/commodity/edit.action")
public String edit(Model model,Commodity commodity){
commodityService.update(commodity);
return list(model);
}
@RequestMapping("/commodity/delete.action")
public String delete(Model model,Integer id){
commodityService.deleteById(id);
return list(model);
}
}
然後主頁home.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>商品銷售系統首頁</title>
</head>
<body>
<h1>商品銷售系統</h1>
<hr/>
<a href="toAdd.action">
<button style="background-color:#173e65;color:#ffffff;width:70px;">新增</button>
</a>
<c:if test="${commodityList!=null}">
<table style="margin-top: 10px;width:700px;text-align:center;" border=1>
<tr>
<td>序號</td><td>商品名稱</td><td>價格</td><td>商品描述</td>
<td>重量</td><td>型號規格</td>
</tr>
<c:forEach items="${commodityList}" var="item" varStatus="status">
<tr>
<td>${status.index+1}</td><td>${item.name }</td>
<td>${item.price}</td><td>${item.desc }</td>
<td>${item.weight}</td><td>${item.model}</td>
</tr>
</c:forEach>
</table>
</c:if>
<c:if test="${commodityList==null}">
<b>搜尋結果為空!</b>
</c:if>
</body>
</html>
預設頁面index.jsp的跳轉路徑為"commodity/home.action":
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>index</title>
</head>
<body>
<script language="JavaScript">
window.location.href = "commodity/home.action";
</script>
</body>
</html>
新增和編輯頁面我們這裡暫時不使用,所以無需編寫。將工程部署至tomcat:
啟動tomcat,在瀏覽器中分別開啟倉儲系統和銷售系統:
目前它們的資料是相互隔離開來的,我們下面使用RabbitMQ來讓兩者進行資料互動,使得銷售系統可以同步倉儲系統的貨物資料。
二、搭建RabbitMQ連線環境
下面分別在倉儲系統和銷售系統中新增RabbitMQ的連線環境,由於配置類似,這裡以“生產者”和“消費者”來區分兩工程的不同配置。
首先我們在pom.xml新增RabbitMQ的相關依賴:
<!-- RabbitMQ相關依賴 -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
儲存後可以看到相關依賴被引入:然後在src/main/resource下建立一個名為“beans-rabbitmq.xml”的配置檔案,為rabbitmq的配置檔案:
編寫該檔案:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd">
</beans>
上面除了spring的bean的schema宣告,還要加入rabbit的schema宣告。然後編寫該配置檔案:
(1)首先新增RabbitMQ的連線工廠(生產者和消費者都配置)
<!-- 定義RabbitMQ的連線工廠 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}"
virtual-host="${rabbitmq.vhost}" />
這裡動態引入了引數配置檔案的資訊,所以這裡需要建立一個properties檔案,用於配置RabbitMQ的連線資訊。所以在src/main/resource下建立一個名為“rabbitmq.properties”的配置檔案,放置連線資訊:內容:
rabbitmq.host=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=jack
rabbitmq.password=jack
rabbitmq.vhost=/jack
然後在spring配置檔案beans.xml的資料庫配置檔案引入後面,引入該properties配置:
<!-- 1.載入資料庫配置的屬性檔案 -->
<context:property-placeholder location="classpath:db.properties,classpath:rabbitmq.properties"/>
然後回到beans-rabbitmq.xml中,在rabbit:connection-factory下新增RabbitMQ的中心管理模組:
<!-- MQ的管理,包括佇列、交換器等 -->
<rabbit:admin connection-factory="connectionFactory" />
(2)定義交換機(僅生產者配置)
這裡我們定義一個“萬用字元”型別的交換機:
<!-- 定義交換機,auto-declare為自動宣告, durable為持久化佇列-->
<rabbit:topic-exchange name="JACK-ITEM-EXCHANGE" auto-declare="true" durable="true"/>
(3)定義模板(僅生產者配置)
下面定義Rabbit模板,該模板會載入獲取與rabbit互動的連線工廠和相關的交換機,在Java應用層可以注入該類進行與RabbitMQ的各種資料互動,十分重要:
<!-- 定義Rabbit模板,指定連線工廠以及定義exchange -->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" exchange="JACK-ITEM-EXCHANGE" />
(4)定義佇列和監聽(僅消費者配置)
這裡需要為消費者的配置檔案中配置接收資訊的佇列配置:
<!-- 定義佇列,auto-declare為自動宣告, durable為持久化佇列 -->
<rabbit:queue name="JACK-WEB-ITEM-QUEUE" auto-declare="true" durable="true"/>
然後是監聽物件和監聽物件配置:
<!-- 消費者物件 -->
<bean id="itemMQHandler" class="com.sale_system.mq.handle.ItemMQHandler"/>
<!-- 監聽物件 -->
<rabbit:listener-container connection-factory="connectionFactory">
<!-- ref為監聽物件、method為有訊息之後通知的方法名 -->
<rabbit:listener ref="itemMQHandler" method="execute" queue-names="JACK-WEB-ITEM-QUEUE"/>
</rabbit:listener-container>
當訊息推送到JACK-WEB-ITEM-QUEUE佇列後,listener-container會通知listener物件itemMQHandler接收資訊,接收方法為“execute”,接收佇列名為“JACK-WEB-ITEM-QUEUE”的資訊。生產者完整的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd">
<!-- 定義RabbitMQ的連線工廠 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}"
virtual-host="${rabbitmq.vhost}" />
<!-- MQ的管理,包括佇列、交換器等 -->
<rabbit:admin connection-factory="connectionFactory" />
<!-- 定義交換機,auto-declare為自動宣告, durable為持久化佇列-->
<rabbit:topic-exchange name="JACK-ITEM-EXCHANGE" auto-declare="true" durable="true"/>
<!-- 定義Rabbit模板,指定連線工廠以及定義exchange -->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" exchange="JACK-ITEM-EXCHANGE" />
</beans>
消費者的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd">
<!-- 定義RabbitMQ的連線工廠 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}"
virtual-host="${rabbitmq.vhost}" />
<!-- MQ的管理,包括佇列、交換器等 -->
<rabbit:admin connection-factory="connectionFactory" />
<!-- 定義佇列,auto-declare為自動宣告, durable為持久化佇列 -->
<rabbit:queue name="JACK-WEB-ITEM-QUEUE" auto-declare="true" durable="true"/>
<!-- 消費者物件 -->
<bean id="itemMQHandler" class="com.sale_system.mq.handle.ItemMQHandler"/>
<!-- 監聽物件 -->
<rabbit:listener-container connection-factory="connectionFactory">
<!-- ref為監聽物件、method為有訊息之後通知的方法名 -->
<rabbit:listener ref="itemMQHandler" method="execute" queue-names="JACK-WEB-ITEM-QUEUE"/>
</rabbit:listener-container>
</beans>
其中的消費者物件和監聽物件,在下面進行監聽邏輯編寫時會詳細講到,這裡可以先註釋。最後,在spring的配置檔案beans.xml的最下面引入該xml:
<!-- 引入RabbitMQ配置 -->
<import resource="classpath*:beans-rabbitmq.xml" />
至此RabbitMQ的基本環境搭建完畢。
這裡細心的朋友可能會注意到,我們並沒有在生產者的配置檔案中宣告佇列並且繫結交換機,其實這一步操作不應該在這裡做,因為後臺一旦進行了配置,則會耦合度比較高,當繫結關係發生變化時,需要重啟專案,十分的不友好。所以,繫結關係最好是手動在RabbitMQ的管理工具裡指定。
登入RabbitMQ的管理工具,開啟Exchanges模組:
建立一個名為“JACK-ITEM-EXCHANGE”的交換機:
然後開啟Queues,建立一個名為“JACK-WEB-ITEM-QUEUE”的佇列:
然後回到Exchanges模組,將佇列繫結在交換機上:
三、倉儲系統對外發布訊息
在之前的業務涉及中,我們要求倉儲系統在新增、刪除以及編輯的時候需要將資訊傳輸至訊息佇列,所以我們在相關方法中新增與rabbit互動的程式碼。
首先在Controller類中注入rabbitTemplate:
//注入RabbitMQ的模板類,用於與RabbitMQ互動
@Autowired
private RabbitTemplate rabbitTemplate;
然後修改貨物新增的Controller方法,在insert之後,我們要與RabbitMQ互動,將新增的資訊推送到交換機:
@RequestMapping("/product/add.action")
public String add(Model model,Product product){
productService.insert(product);
try {
//將資訊資訊傳送至rabbitMQ的交換機,通知其它系統新增了商品
Map<String,Object> msg = new HashMap<String,Object>();
msg.put("itemObject",product);
msg.put("type", "insert");
msg.put("date", System.currentTimeMillis());//時間戳
//使用fastJson將新增的商品資訊轉換為json字串,方便接收方解析
this.rabbitTemplate.convertAndSend("item.insert", JSON.toJSON(msg).toString());//訊息的key,與內容
} catch (AmqpException e) {
e.printStackTrace();
}
//重新重新整理至分頁列表頁首頁
return list(model);
}
可以看到我們組裝了一個Map,將變更的貨物資訊、操作型別、時間戳封裝在Map中,然後轉換為json傳輸到交換機。這裡要做兩個操作,由於之前編寫MyBatis的時候沒有指定自動返回主鍵ID,這裡需要在Mapper配置檔案中的insert配置中新增useGeneratedKeys、keyProperty、keyColumn三個引數:
<insert id="insert" parameterType="com.warehouse_management.po.Product" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into product
(PNAME,PRICE,PDESC,WEIGHT,MODEL)
values
(
#{name,jdbcType=VARCHAR},
#{price,jdbcType=DOUBLE},
#{desc},
#{weight,jdbcType=INTEGER},
#{model,jdbcType=VARCHAR}
)
</insert>
這樣進行新增操作後,product類中就會拿到新增到資料庫裡該條資料的id主鍵。然後由於我們要封裝和解析json,在兩個工程的POM檔案中都新增fastJson的依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
之後在倉儲系統的編輯與刪除Controller想贏方法中同樣新增訊息通知的程式碼:
@RequestMapping("/product/edit.action")
public String edit(Model model,Product product){
productService.update(product);
try {
//將資訊資訊傳送至rabbitMQ的交換機,通知其它系統修改了商品
Map<String,Object> msg = new HashMap<String,Object>();
msg.put("itemObject",product);
msg.put("type", "update");
msg.put("date", System.currentTimeMillis());//時間戳
//使用fastJson將新增的商品資訊轉換為json字串,方便接收方解析
this.rabbitTemplate.convertAndSend("item.update", JSON.toJSON(msg).toString());//訊息的key,與內容
} catch (AmqpException e) {
e.printStackTrace();
}
//重新重新整理至分頁列表頁首頁
return list(model);
}
@RequestMapping("/product/delete.action")
public String delete(Model model,Integer id){
productService.deleteById(id);
try {
//將資訊資訊傳送至rabbitMQ的交換機,通知其它系統刪除了商品
Product product = new Product();
product.setId(id);//封裝刪除的ID資訊
Map<String,Object> msg = new HashMap<String,Object>();
msg.put("itemObject",product);
msg.put("type", "update");
msg.put("date", System.currentTimeMillis());//時間戳
//使用fastJson將新增的商品資訊轉換為json字串,方便接收方解析
this.rabbitTemplate.convertAndSend("item.update", JSON.toJSON(msg).toString());//訊息的key,與內容
} catch (AmqpException e) {
e.printStackTrace();
}
//重新重新整理至分頁列表頁首頁
return list(model);
}
其實發現程式碼有重複性,我們可以將它單獨封裝為一個方法。封裝後最終的程式碼為:@RequestMapping("/product/add.action")
public String add(Model model,Product product){
productService.insert(product);
//將資訊資訊傳送至rabbitMQ,通知其它系統新增了商品
sengMsgToMQ(product,"insert");
//重新重新整理至分頁列表頁首頁
return list(model);
}
@RequestMapping("/product/edit.action")
public String edit(Model model,Product product){
productService.update(product);
//將資訊資訊傳送至rabbitMQ,通知其它系統編輯了商品
sengMsgToMQ(product,"update");
//重新重新整理至分頁列表頁首頁
return list(model);
}
@RequestMapping("/product/delete.action")
public String delete(Model model,Integer id){
productService.deleteById(id);
//將資訊資訊傳送至rabbitMQ,通知其它系統刪除了商品
Product product = new Product();
product.setId(id);//封裝刪除的ID資訊
sengMsgToMQ(product,"delete");
//重新重新整理至分頁列表頁首頁
return list(model);
}
private void sengMsgToMQ(Product product,String Type) {
try {
Map<String,Object> msg = new HashMap<String,Object>();
msg.put("itemObject",JSON.toJSON(product).toString());
msg.put("type", Type);
msg.put("date", System.currentTimeMillis());//時間戳
//使用fastJson將新增的商品資訊轉換為json字串,方便接收方解析
this.rabbitTemplate.convertAndSend("item."+Type, JSON.toJSON(msg).toString());//訊息的key,與內容
} catch (AmqpException e) {
e.printStackTrace();
}
}
至此,倉儲系統的互動邏輯完成。下面要編寫銷售系統與RabbitMQ的互動,用於獲取倉儲系統同步的資料。
四、銷售系統同步訊息佇列商品資訊
在銷售系統中要時刻保持與倉儲系統中的貨物資訊一致,所以這裡需要編寫一個消費者,用於從RabbitMQ中實時監聽資料變更資訊。
首先建立一個“com.sale_system.mq.handle”包,用於放置與MQ相關的控制器類。然後建立一個名為“ItemMQHandler”的類,用於實現資訊監聽:
package com.sale_system.mq.handle;
public class ItemMQHandlerr {
public void execute(String msg){
}
}
然後在銷售系統的mq的xml配置檔案中新增該bean:
<!-- 消費者物件 -->
<bean id="itemMQHandler" class="com.sale_system.mq.handle.ItemMQHandler"/>
然後定義一個監聽器,用來監聽RabbitMQ中指定的佇列:
<!-- 監聽物件 -->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="itemMQHandler" method="execute" queue-names="JACK-WEB-ITEM-QUEUE"/><!-- 監聽物件和有訊息之後通知的方法名 -->
</rabbit:listener-container>
注:上面其實已經添加了,這裡再次講解一下,加深印象。佇列與交換機已經人工在管理介面中綁定了,這裡不再贅述。
然後我們回到ItemMQHandler類中進行資訊的具體處理:
package com.sale_system.mq.handle;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mysql.jdbc.StringUtils;
import com.sale_system.po.Commodity;
import com.sale_system.service.CommodityService;
public class ItemMQHandler {
@Autowired
private CommodityService commodityService;
/**
* 更新資料庫中的商品資料,完成資料同步
* */
public void execute(String msg){
if(!StringUtils.isNullOrEmpty(msg)){
Map<String,Object> msgMap = (Map<String, Object>) JSON.parse(msg);
//獲取商品同步資訊
String jsonStr = (String) msgMap.get("itemObject");
JSONObject jsonObject = JSON.parseObject(jsonStr);
Commodity commodity = convertMapToCommodity(jsonObject);
//獲取同步型別
String type = (String) msgMap.get("type");
if(type.equals("insert")){
commodityService.insert(commodity);
}else if(type.equals("update")){
if(commodityService.selectById(commodity.getId())!=null){
//如果ID存在,進行編輯
commodityService.update(commodity);
}else{
//如果ID不存在,執行插入操作
commodityService.insert(commodity);
}
}else if(type.equals("delete")){
if(commodityService.selectById(commodity.getId())!=null){
//如果ID存在,進行刪除
commodityService.deleteById(commodity.getId());
}
}
}
}
private Commodity convertMapToCommodity(JSONObject jsonObject) {
Commodity commodity = new Commodity();
if(jsonObject!=null){
if(jsonObject.get("id")!=null){
commodity.setId(Integer.parseInt(jsonObject.get("id").toString()));
}
if(jsonObject.get("name")!=null){
commodity.setName(jsonObject.get("name").toString());
}
if(jsonObject.get("price")!=null){
commodity.setPrice(Double.parseDouble(jsonObject.get("price").toString()));
}
if(jsonObject.get("desc")!=null){
commodity.setDesc(jsonObject.get("desc").toString());
}
if(jsonObject.get("weight")!=null&&!StringUtils.isNullOrEmpty(jsonObject.get("weight").toString())){
commodity.setWeight(Integer.parseInt(jsonObject.get("weight").toString()));
}
if(jsonObject.get("model")!=null){
commodity.setModel(jsonObject.get("model").toString());
}
}
return commodity;
}
}
接受到資訊後,首先取出變更商品的實體資訊,轉換為商品類,然後根據type進行不同的操作來更新資料。我們重啟倉庫系統和銷售系統,登入後依然沒有變化:
但是我們可以在控制檯看到,銷售系統正在實時監聽訊息佇列:
此時我們在倉儲系統中插入一條新的資料:
檢視資料庫,也進行了資料儲存:
然後我們修改前三個資料:
此時根據編輯空則更新的規則,發現銷售系統也同步過來了:
至此,我們實現了兩個系統之間的資料同步操作,同時RabbitMQ的基本知識也介紹完畢,有興趣的同學可以繼續深入研究。