日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

ssm知识点总结

發(fā)布時(shí)間:2025/3/21 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ssm知识点总结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

項(xiàng)目名稱:教育網(wǎng)—在線調(diào)查系統(tǒng)

項(xiàng)目總體流程圖:

設(shè)計(jì)調(diào)查:調(diào)查-->包裹--->問(wèn)題(增刪改查)

1.調(diào)整包裹順序

2.移動(dòng)復(fù)制包裹

3.深度刪除

創(chuàng)建調(diào)查流程分析:

主要生成survey_id、survey_name、completed(是否完成)、logoPath(涉及到圖片上傳)

?

?springMVC文件上傳:

文件上傳對(duì)表單的要求 ①form標(biāo)簽的enctype屬性:multipart/form-data ②form標(biāo)簽的method屬性:post ③生成文件上傳框:input type="file"


文件的保存
①調(diào)用multiPartFile.transfer()方法
②文件的路徑不能使用絕對(duì)的物理路徑
<img src="E:\good.jpg"/>
這樣的路徑瀏覽器無(wú)法顯示圖片
③有效的路徑形式


<img src="surveyLogos/logo.gif"/>


這個(gè)路徑有效是因?yàn)樗且粋€(gè)虛擬路徑。
④虛擬路徑VS真實(shí)物理路徑
[1]真實(shí)物理路徑:Web應(yīng)用中的文件和目錄在硬盤(pán)上保存的真實(shí)路徑(注意:這里指的是部署目錄)。


D:\WorkSpaceShenZhen170228\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\Survey_1_UI\surveyLogos\logo.gif


瀏覽器不能直接訪問(wèn)這個(gè)路徑,所以需要由服務(wù)器將它轉(zhuǎn)換為瀏覽器可以訪問(wèn)的虛擬路徑
Web應(yīng)用在不同的操作系統(tǒng)下、在不同的服務(wù)器上部署時(shí)真實(shí)物理路徑是有可能變化的。

[2]虛擬路徑:服務(wù)器虛擬出來(lái)供瀏覽器訪問(wèn)的路徑,以主機(jī)地址為基準(zhǔn)的


http://localhost:8080/Survey_1_UI/surveyLogos/logo.gif


不管Web應(yīng)用部署在什么操作系統(tǒng)的什么服務(wù)器上,虛擬路徑都是相同的。

⑤在handler方法中保存文件時(shí)如何將文件保存到img標(biāo)簽可以訪問(wèn)的路徑下


[1]保存文件的目標(biāo)路徑一定在部署目錄下
[2]部署目錄會(huì)隨著部署的服務(wù)器、操作系統(tǒng)不同而發(fā)生變化
[3]所以要通過(guò)不變的虛擬路徑動(dòng)態(tài)生成有可能變化的真實(shí)物理路徑


String 真實(shí)物理路徑 = servletContext.getRealPath(虛擬路徑);


⑥壓縮圖片


[1]直接復(fù)制一個(gè)工具方法resizeImages()
[2]兩個(gè)需要手動(dòng)導(dǎo)入的API
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.image.codec.jpeg.JPEGCodec;


[3]傳入的參數(shù)
inputStream:上傳文件的輸入流
realPath:/surveyLogos目錄的真實(shí)路徑,后面沒(méi)有斜杠,而且不帶具體文件名
[4]返回值:可以直接用于設(shè)置Survey對(duì)象的logoPath屬性

文件數(shù)據(jù)的驗(yàn)證

①驗(yàn)證的內(nèi)容

[1]文件的大小 [2]文件的類型 ②實(shí)現(xiàn)方式 [1]檢測(cè)用戶是否上傳了文件 [2]獲取相關(guān)數(shù)據(jù):文件大小、文件內(nèi)容類型 [3]如果檢測(cè)到大小或類型不符合要求,則拋出對(duì)應(yīng)的異常 分頁(yè)顯示我未完成的調(diào)查 ①分頁(yè)支持:MyBatis插件PageHelper ②要查詢的數(shù)據(jù):Survey對(duì)象 [1]限制條件1:當(dāng)前用戶 [2]限制條件2:未完成 ③SurveyMapper.selectAllSurvey(userId,completed);

考慮到將來(lái)也會(huì)查詢所有已完成的調(diào)查,所以u(píng)serId和completed都需要傳入

?

更新操作的特殊要求: [1]用戶沒(méi)有上傳文件時(shí)保持舊的logo_path字段值不變 [2]用戶如果上傳了文件那么就將logo_path字段值修改為新值 [3]用戶如果上傳了不符合要求的圖片要回到更新調(diào)查的表單頁(yè)面并顯示錯(cuò)誤消息 [4]回到更新調(diào)查的表單頁(yè)面顯示錯(cuò)誤消息時(shí)要保證表單上模型數(shù)據(jù)回顯正常 [5]更新完成后回到分頁(yè)頁(yè)面,且回到的是之前所在的頁(yè)碼
[6]文件上傳驗(yàn)證失敗后,再正常更新還是能夠回到之前所在的分頁(yè)頁(yè)面

包裹的CRUD

包裹的序號(hào)默認(rèn)采用包裹的id

?原理:通過(guò)mybatis的xml映射文件獲取自增主鍵獲取包裹的序號(hào),如果采用插入后查詢id最大值賦值給Order會(huì)因?yàn)榫€程問(wèn)題出錯(cuò)。

[1]錯(cuò)誤的做法
  • 保存bag對(duì)象
  • 查詢guest_bag表中bag_id的最大值
  • 使用這個(gè)最大值設(shè)置bag_order
[2]為什么是錯(cuò)誤的?在并發(fā)的情況下,假設(shè)有T1和T2兩個(gè)線程
  • T1:保存bag對(duì)象(bag_id的最大值是6)
  • T2:保存bag對(duì)象(bag_id的最大值是7)
  • T1:查詢最大值,得到的結(jié)果:7
  • T1:設(shè)置bag_order為7就錯(cuò)了
  • T2……
[3]正確的做法
  • T1:保存bag對(duì)象,立即獲取剛剛自增產(chǎn)生的bag_id——6
  • T2:保存bag對(duì)象,立即獲取剛剛自增產(chǎn)生的bag_id——7
  • T1:使用已經(jīng)獲取到的自增主鍵值設(shè)置bag_order為6
  • T2:使用已經(jīng)獲取到的自增主鍵值設(shè)置bag_order為7

③獲取自增主鍵值的方式以及相關(guān)UPDATE語(yǔ)句

useGeneratedKeys="true"?keyProperty="bagId"
update guest_bag set bag_order=#{bagId} where bag_id=#{bagId}

創(chuàng)建問(wèn)題的流程分析

?

難點(diǎn):將選項(xiàng)轉(zhuǎn)化為json進(jìn)行處理。

對(duì)選項(xiàng)進(jìn)行特殊處理的四個(gè)方法

DataprocessUtils.processOptionToJson(Question question);

判斷題型,簡(jiǎn)答題不處理 將option字符串根據(jù)“\r\n”拆分為數(shù)組 借助于工具將數(shù)組轉(zhuǎn)換為JSON字符串 DataprocessUtils.processOptionFromJson(Question question); 判斷題型,簡(jiǎn)答題不處理 借助于工具將JSON格式的option字符串還原為L(zhǎng)ist 將List組合成以“\r\n”分開(kāi)的字符串 Question.getOptionList(); 借助于工具將JSON格式的option字符串還原為L(zhǎng)ist DataprocessUtils.convertJSONToList(String json);

將重復(fù)操作提取出來(lái)

答案回顯:type1,2,3

?

包裹和問(wèn)題數(shù)據(jù)的來(lái)源

答案數(shù)據(jù)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu):

Session

  allBagMap

    根據(jù)bagId→paramMap

      根據(jù)表單標(biāo)簽的name屬性值→values數(shù)組

        根據(jù)values數(shù)組進(jìn)行標(biāo)簽的回顯

checkbox radio text

?

四個(gè)按鈕相關(guān) ①這是四個(gè)提交按鈕,而且他們提交的是同一個(gè)表單,同一個(gè)Handler方法來(lái)處理 ②四個(gè)按鈕如何區(qū)分

<input type="submit"?name="submit_prev"?value="返回上一個(gè)包裹"/>

<input type="submit" name="submit_next" value="進(jìn)入下一個(gè)包裹"/>

<input type="submit" name="submit_quit" value="放棄"/>

<input type="submit" name="submit_done" value="完成"/> 點(diǎn)擊任何一個(gè)提交按鈕都會(huì)將這個(gè)提交按鈕的name、value提交給服務(wù)器 在Handler方法中檢查請(qǐng)求參數(shù)Map中是否存在對(duì)應(yīng)的name值就能夠區(qū)分了 boolean contains = parameterMap.containsKey("submit_prev"); if(contains){

//說(shuō)明用戶點(diǎn)擊的是"返回上一個(gè)包裹"

}

③四個(gè)按鈕的顯示條件

[1]返回上一個(gè):當(dāng)前包裹索引>0 [2]進(jìn)入下一個(gè):當(dāng)前包裹索引<size-1

size-1實(shí)際上就是最后一個(gè)包裹的索引

[3]放棄:無(wú)條件 [4]完成:當(dāng)前包裹索引 == size-1

1、使用異常映射機(jī)制統(tǒng)一管理項(xiàng)目中錯(cuò)誤消息

why?

常規(guī)的是當(dāng)不符合業(yè)務(wù)情況時(shí)產(chǎn)生異常信息返回,但容易因?yàn)閭€(gè)人書(shū)寫(xiě)代碼的行為習(xí)慣導(dǎo)致,編程混亂,

會(huì)增加交流的成本,降低開(kāi)發(fā)效率。所以需要采用異常映射機(jī)制統(tǒng)一管理錯(cuò)誤消息。

if(錯(cuò)誤條件){

  

map.put("message","對(duì)不起,這個(gè)用戶名已經(jīng)被占用了,請(qǐng)重新注冊(cè)!");

  return "頁(yè)面";

}

how?

異常映射機(jī)制統(tǒng)一管理項(xiàng)目錯(cuò)誤信息:

  拿注冊(cè)用戶名字存在為例:

?

//已存在則拋出異常
if
(adminCount > 0) {throw new AdminNameExistsException(GlobalMessage.ADMIN_NAME_EXISTS);} AdminNameExistsException是自定義的Exception
public class AdminNameExistsException extends RuntimeException {private static final long serialVersionUID = 1L;public AdminNameExistsException(String message) {super(message);}}

異常映射機(jī)制在spring.xml中進(jìn)行異常映射:映射到相應(yīng)頁(yè)面

<!--簡(jiǎn)單異常映射解析器,對(duì)于用戶 名存在throw的異常進(jìn)行映射跳轉(zhuǎn)到指定視圖 --><bean id="SimpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="exceptionMappings"><!-- key屬性是異常類型 --><!-- 標(biāo)簽體配置目標(biāo)視圖 --><props><prop key="com.lamsey.survey.e.UserNameAlreadyExistException">guest/user_regist</prop><prop key="com.lamsey.survey.e.UserLoginFailedException">guest/user_login</prop><prop key="com.lamsey.survey.e.UserAccessForbiddenException">guest/user_login</prop><prop key="com.lamsey.survey.e.FileTypeInvalidForSaveException">guest/survey_addUI</prop><prop key="com.lamsey.survey.e.FileTooLargeForSaveException">guest/survey_addUI</prop><prop key="com.lamsey.survey.e.FileTypeInvalidForEditException">guest/survey_editUi</prop><prop key="com.lamsey.survey.e.FileTooLargeForEditException">guest/survey_editUi</prop><prop key="com.lamsey.survey.e.RemoveSurveyException">error</prop><prop key="com.lamsey.survey.e.RemoveBagException">error</prop><prop key="com.lamsey.survey.e.SurveyWithoutAnyBagException">error</prop><prop key="com.lamsey.survey.e.SurveyHasEmptyBagException">error</prop><prop key="com.lamsey.survey.e.BagOrderDuplicateException">guest/bag_AdjustUI</prop><prop key="com.lamsey.survey.e.AdminLoginFailedException">manager/admin_login</prop><prop key="com.lamsey.survey.e.HasNoAuthorityException">error</prop><prop key="com.lamsey.survey.e.AdminAccessForbiddenException">error</prop></props> </property></bean>

頁(yè)面對(duì)異常進(jìn)行捕獲顯示:

<c:if test="${requestScope.exception != null }"><%-- request.setAttribute("exception",exception) --%><%-- request.getAttribute("exception") --%><%-- exception.getMessage() --%><div class="form-group"> ${requestScope.exception.message}</div> </c:if>

?

jsp四大域?qū)ο蠼?jīng)常用來(lái)保存數(shù)據(jù)信息。

?

pageContext ?????????? 可以保存數(shù)據(jù)在同一個(gè)jsp頁(yè)面中使用

request?????????????????????? 可以保存數(shù)據(jù)在同一個(gè)request對(duì)象中使用。經(jīng)常用于在轉(zhuǎn)發(fā)的時(shí)候傳遞數(shù)據(jù)

session?????????????????????? 可以保存在一個(gè)會(huì)話中使用

application(ServletContext)???? 就是ServletContext對(duì)象

?jsp 九大內(nèi)置對(duì)象分別是:

request 對(duì)象?? ??? 請(qǐng)求對(duì)象,可以獲取請(qǐng)求信息

response 對(duì)象??????? 響應(yīng)對(duì)象。可以設(shè)置響應(yīng)信息

pageContext 對(duì)象 當(dāng)前頁(yè)面上下文對(duì)象。可以在當(dāng)前上下文保存屬性信息

session 對(duì)象??????????? 會(huì)話對(duì)象。可以獲取會(huì)話信息。

exception 對(duì)象 ???? 異常對(duì)象只有在jsp頁(yè)面的page 指令中設(shè)置 isErrorPage="true" 的時(shí)候才會(huì)存在

application 對(duì)象???? ServletContext對(duì)象實(shí)例,可以獲取整個(gè)工程的一些信息。

config 對(duì)象????????????? ServletConfig對(duì)象實(shí)例,可以獲取Servlet的配置信息

out 對(duì)象?????????????????? 輸出流。

page 對(duì)象??????????????? 表示當(dāng)前Servlet對(duì)象實(shí)例(無(wú)用,用它不如使用this對(duì)象)。

九大內(nèi)置對(duì)象,都是我們可以在【代碼腳本】中或【表達(dá)式腳本】中直接使用的對(duì)象。

2、通過(guò)序列化和反序列化技術(shù)實(shí)現(xiàn)對(duì)象的深度復(fù)制

為什么用深度復(fù)制?

若我們系統(tǒng)中存在大量的對(duì)象是通過(guò)拷貝生成的,如果我們每一個(gè)類都寫(xiě)一個(gè)clone()方法,并將還需要進(jìn)行深拷貝,新建大量的對(duì)象,這個(gè)工程是非常大的,

這里我們可以利用序列化來(lái)實(shí)現(xiàn)對(duì)象的拷貝。

復(fù)制包裹,

執(zhí)行深度復(fù)制
Bag targetBag = (Bag)DataprocessUtils.deeplyCopy(sourceBag);

?如何利用序列化來(lái)完成對(duì)象的拷貝呢?

在內(nèi)存中通過(guò)字節(jié)流的拷貝是比較容易實(shí)現(xiàn)的。把母對(duì)象寫(xiě)入到一個(gè)字節(jié)流中,再?gòu)淖止?jié)流中將其讀出來(lái),

這樣就可以創(chuàng)建一個(gè)新的對(duì)象了,并且該新對(duì)象與母對(duì)象之間并不存在引用共享的問(wèn)題,真正實(shí)現(xiàn)對(duì)象的深拷貝。

?

1.首先將數(shù)據(jù)進(jìn)行序列化

deeplyCopy(Serializable source)

2.將序列化的數(shù)據(jù) /*** 通過(guò)序列化和反序列的方式對(duì)對(duì)象進(jìn)行深度復(fù)制*///深克隆: 具有相同的值,但是兩個(gè)全新的對(duì)象實(shí)例,相互之間不會(huì)受影響// 被復(fù)制對(duì)象的所有變量都含有與原來(lái)的對(duì)象相同的值,除去那些引用其他對(duì)象的變量。// 那些引用其他對(duì)象的變量將指向被復(fù)制過(guò)的新對(duì)象,而不再是原有的那些被引用的對(duì)象。public static Object deeplyCopy(Serializable source){if(source == null) {return null;}//1.聲明一個(gè)變量用來(lái)保存復(fù)制得到的目標(biāo)對(duì)象Object targetObject = null;//2.聲明四個(gè)變量用來(lái)保存四個(gè)流ObjectInputStream ois =null;ObjectOutputStream oos = null;ByteArrayInputStream bais = null;ByteArrayOutputStream baos = null;//3.try...catch...finally結(jié)構(gòu)try{//4.創(chuàng)建字節(jié)數(shù)組輸出流baos = new ByteArrayOutputStream();//5.根據(jù)字節(jié)數(shù)組輸出流創(chuàng)建對(duì)象輸出流oos = new ObjectOutputStream(baos);//6.執(zhí)行對(duì)象的序列化操作(本質(zhì):將對(duì)象序列化后得到的數(shù)據(jù)寫(xiě)入字節(jié)數(shù)組) oos.writeObject(source);//7.獲取保存了序列化數(shù)據(jù)的字節(jié)數(shù)組byte[] byteArray = baos.toByteArray();//8.創(chuàng)建字節(jié)數(shù)組輸入流bais = new ByteArrayInputStream(byteArray);//9.根據(jù)字節(jié)數(shù)組輸入流創(chuàng)建對(duì)象輸入流ois = new ObjectInputStream(bais);//10.執(zhí)行反序列化操作targetObject = ois.readObject();}catch(Exception e){e.printStackTrace();} finally{//11.釋放資源if(oos != null){try{oos.close();}catch(Exception e){e.printStackTrace();}}if(ois != null){try{ois.close();}catch(Exception e){e.printStackTrace();}}} return targetObject;}

?

?

3、使用pageHelper對(duì)商品結(jié)果進(jìn)行分頁(yè)瀏覽功能

why?

普通的sql語(yǔ)句分頁(yè):

limit x,y;

#x:起始數(shù)據(jù)行,y:要查詢的數(shù)據(jù)行

SELECT last_name,salary FROM employees ORDER BY salary DESC #分頁(yè) (寫(xiě)在order by的后面) #limit 0,10; LIMIT 20,10;#21-30段數(shù)據(jù):第3頁(yè) #公式:limit (pageNo - 1) * pageSize , pageSize;

使用普通分頁(yè)太麻煩了,利用mybatis的pageHelper插件會(huì)更容易操作。

how?

public PageInfo<Survey> getSurveyPage(Integer userId, boolean completed, Integer pageNum) {//設(shè)置每頁(yè)顯示數(shù)量int pageSize = 5;PageHelper.startPage(pageNum, pageSize);//執(zhí)行分頁(yè)查詢List<Survey> list = surveyMapper.selectAllSurvey(userId, completed);//用PageInfo對(duì)結(jié)果進(jìn)行包裝int navigatePages = 6;PageInfo<Survey> page = new PageInfo<>(list, navigatePages);return page;}

?4.JFreeChart將選擇題的答案數(shù)據(jù)導(dǎo)出為餅圖

why?

JFreeChart是JAVA平臺(tái)上的一個(gè)開(kāi)放的圖表繪制類庫(kù)。它完全使用JAVA語(yǔ)言編寫(xiě),是為applications, applets, servlets 以及JSP等使用所設(shè)計(jì)。

JFreeChart可生成餅圖(pie charts)、柱狀圖(bar charts)、散點(diǎn)圖(scatter plots)、時(shí)序圖(time series)、甘特圖(Gantt charts)等等

多種圖表,并且可以產(chǎn)生PNG和JPEG格式的輸出,還可以與PDF和EXCEL關(guān)聯(lián)。

因?yàn)橐獙?duì)每道題統(tǒng)計(jì)數(shù)據(jù),所以采用餅狀圖進(jìn)行顯示每道選擇題的結(jié)果。簡(jiǎn)答題

how?

?

@RequestMapping(value="manager/statistics/showAnswerChart/{questionId}",method=RequestMethod.GET)public void showAnswerChart(@PathVariable(value="questionId") Integer questionId,HttpServletResponse response) throws IOException{//1.調(diào)用Service方法生成JFreeChart對(duì)象JFreeChart chart = statisticsService.getChart(questionId);//2.將JFreeChart對(duì)象生成的圖表圖片返回給瀏覽器//通過(guò)response對(duì)象獲取一個(gè)能夠給瀏覽器返回?cái)?shù)據(jù)的輸出流ServletOutputStream outputStream = response.getOutputStream();//借助ChartUtilities工具類的方法將圖表數(shù)據(jù)寫(xiě)入到上面獲取的輸出流ChartUtilities.writeChartAsJPEG(outputStream, chart, 1200, 600);//③當(dāng)前Handler方法通過(guò)上面的輸出流已經(jīng)能夠給瀏覽器明確的響應(yīng)數(shù)據(jù),所以不再前往任何一個(gè)視圖//所以沒(méi)有任何返回值}

JFreeChart對(duì)象的創(chuàng)建

public JFreeChart getChart(Integer questionId) {//獲取題目數(shù)據(jù)Question question = questionMapper.selectByPrimaryKey(questionId);int count = answerMapper.selectQuestionEngagedCount(questionId);//獲取圖例區(qū)數(shù)據(jù)List<String> optionList = question.getOptionList();//獲取標(biāo)簽區(qū)數(shù)據(jù)Map<String, Object> map = new HashMap<>();for(int index= 0;index < optionList.size();index++){//(1)option作為標(biāo)簽名String option = optionList.get(index);//(2)index結(jié)合questionId查詢optionEngagedCountString optionValue = "%," + index + ",%";int optionCount = answerMapper.SelectOptionEngagedCount(questionId,optionValue);map.put(option, optionCount);}String title = question.getQuestionName()+count+"次參與";Object chart = DataprocessUtils.generateChart(title, map);return (JFreeChart) chart;}     //通過(guò)response對(duì)象獲取一個(gè)能夠給瀏覽器返回?cái)?shù)據(jù)的輸出流ServletOutputStream outputStream = response.getOutputStream();//借助ChartUtilities工具類的方法將圖表數(shù)據(jù)寫(xiě)入到上面獲取的輸出流ChartUtilities.writeChartAsJPEG(outputStream, chart, 1200, 600);
統(tǒng)計(jì)答案中的數(shù)據(jù),
SELECT COUNT(*) FROM guest_answer WHERE question_id = 19 AND CONCAT(",", answer_content, ",") LIKE '%,1,%' 1)分析采用字符串,所以采用like來(lái)進(jìn)行匹配 SELECT COUNT(*) FROM guest_answer WHERE question_id = 19 AND answer_content ?LIKE '%1%' 但這樣查詢存在一個(gè)問(wèn)題,如存在10或者21也會(huì)統(tǒng)計(jì)進(jìn)去,會(huì)導(dǎo)致特殊問(wèn)題。 2)更改為?LIKE '%,1,%'會(huì)使前后沒(méi)有逗號(hào)的匹配不到,造成遺漏 所以在查詢選項(xiàng)前先加上兩個(gè)逗號(hào) SELECT COUNT(*) FROM guest_answer WHERE question_id = 19 AND CONCAT(",", answer_content, ",") LIKE '%,1,%' answer_context

總結(jié):首先創(chuàng)建JFreeChart對(duì)象(title,各個(gè)選項(xiàng)的count存進(jìn)map里面),然后借助ChartUtilities工具類的方法將圖表數(shù)據(jù)寫(xiě)入文件到指定目的地

?創(chuàng)建response的outPutStream進(jìn)行輸出到瀏覽器

5.使用POI匯總數(shù)據(jù),并將整個(gè)調(diào)查參與的結(jié)果導(dǎo)出為Excel表格

why?

為了將所有調(diào)查問(wèn)卷的數(shù)據(jù)進(jìn)行收集

how?

①POI技術(shù)本身 [1]數(shù)據(jù)→Excel [2]Excel→數(shù)據(jù)

②項(xiàng)目中將數(shù)據(jù)導(dǎo)出為Excel的數(shù)據(jù)來(lái)源

③羅列所需要的數(shù)據(jù) [1]從URL地址中匹配surveyId [2]根據(jù)surveyId深度加載Survey對(duì)象 [3]根據(jù)Survey對(duì)象中的包裹、問(wèn)題數(shù)據(jù)創(chuàng)建List<Question> [4]根據(jù)surveyId查詢所有答案數(shù)據(jù):List<Answer> [5]根據(jù)surveyId查詢surveyEngagedCount

④生成Excel文件所需要的數(shù)據(jù)的要求

?

?

⑤符合要求的數(shù)據(jù)結(jié)構(gòu)

?

  /*** 導(dǎo)出excel表* @throws IOException */@RequestMapping(value="manager/survey/exportExcel/{surveyId}",method=RequestMethod.GET)public void exportExcel(@PathVariable(value="surveyId") Integer surveyId,HttpServletResponse response) throws IOException{//1.生成excel對(duì)象HSSFWorkbook workbook = statisticsService.getWorkBook(surveyId);//2.將Excel文件以下載形式返回給瀏覽器//i.設(shè)置響應(yīng)數(shù)據(jù)的內(nèi)容類型response.setContentType("application/vnd.ms-excel");//ii.生成文件名String filename = System.nanoTime()+".xls";//iii.在響應(yīng)消息頭中設(shè)置文件名response.setHeader("Content-Disposition", "attachment;filename="+filename);//iv.獲取一個(gè)能夠給瀏覽器返回二進(jìn)制數(shù)據(jù)的輸出流ServletOutputStream outputStream = response.getOutputStream();//v.將workbook對(duì)象寫(xiě)入這個(gè)輸出流 workbook.write(outputStream);} //1.生成excel對(duì)象 public HSSFWorkbook getWorkBook(Integer surveyId) throws FileNotFoundException {//1.獲取數(shù)據(jù)//2.建表HSSFWorkbook workbook = new HSSFWorkbook();//獲取表名//獲取題目數(shù)據(jù),構(gòu)建excel表名Survey survey = surveyMapper.getSurveyDeeply(surveyId);String surveyName = survey.getSurveyName();int count = answerMapper.getSurveyEngagedCount(surveyId);String sheetName = surveyName+"共有"+count+"調(diào)查";HSSFSheet sheet = workbook.createSheet(sheetName);//iv.如果surveyEngagedCount被參與的次數(shù)為零,則停止函數(shù)執(zhí)行if(count == 0) {return workbook;} //創(chuàng)建首行,包括行標(biāo)題//1.遍歷所有題目填進(jìn)第一行LinkedHashSet<Bag> bagSet = survey.getBagSet();List<Question> questionList = new ArrayList<>();for(Bag bag:bagSet){LinkedHashSet<Question> questionSet = bag.getQuestionSet();//把set轉(zhuǎn)化為L(zhǎng)ist方便索引一一取出 questionList.addAll(questionSet);}//填寫(xiě)首行HSSFRow firstRow = sheet.createRow(0);for(int i=0;i<questionList.size();i++){Question question = questionList.get(i);String questionName = question.getQuestionName();HSSFCell cell = firstRow.createCell(i);cell.setCellValue(questionName); }//填充所有行答案數(shù)據(jù)//查出所有批次的answerContext//answerContext必須要與questionId一一對(duì)應(yīng)//uuid questionId answerContext//[4]根據(jù)surveyId查詢所有答案數(shù)據(jù):List<Answer>List<Answer> answerList = answerMapper.selectAnswerListBySurveyId(surveyId);//2.轉(zhuǎn)換數(shù)據(jù)格式Map<String, Map<Integer, String>> bigMap = getBigMap(answerList); //填充答案行//按照questionList中一一查出的id對(duì)smallMap進(jìn)行取值,從而一一對(duì)應(yīng)//v.從bigMap中獲取values部分Collection<Map<Integer,String>> values = bigMap.values();//vi.將values轉(zhuǎn)換為L(zhǎng)ist集合List<Map<Integer,String>> smallMapList = new ArrayList(values);//遍歷smallMapList//Map<uuid, Map<questionId, answerContext>> bigMap//uuid-->對(duì)應(yīng)一行的questionId,所以u(píng)uid的數(shù)目為行(即smallMapList.size()),以questionId遍歷question單元格for(int i=0;i<smallMapList.size();i++){//獲取第一個(gè)Map<Integer, String> smallMap = smallMapList.get(i);//viii.這里注意:i控制行索引int rowIndex = i + 1;//ix.根據(jù)rowIndex創(chuàng)建行HSSFRow row = sheet.createRow(rowIndex);//x.創(chuàng)建具體單元格for(int j=0;j<questionList.size();j++){HSSFCell cell = row.createCell(j);//xi.以j為索引從questionList中獲取Question對(duì)象Question question = questionList.get(j);//xii.從Question對(duì)象中獲取questionIdInteger questionId = question.getQuestionId();//xiii.以questionId為鍵從smallMap中獲取對(duì)應(yīng)的答案內(nèi)容String context = smallMap.get(questionId); //xiv.用content設(shè)置當(dāng)前單元格內(nèi)容 cell.setCellValue(context);}}return workbook;}

把所有答案內(nèi)容進(jìn)行處理:

//根據(jù)answerList將數(shù)據(jù)轉(zhuǎn)換為適合生成Excel表的形式//一個(gè)uuid對(duì)應(yīng)一套的questionId,所以smallMap中的questionId只要相同就要賦值給一樣的smallMap元素//不停創(chuàng)建map,得到不同的地址,相同的uuid的smallMap指向同一個(gè)地址private Map<String, Map<Integer, String>> getBigMap(List<Answer> answerList) {//1.創(chuàng)建空的bigMapMap<String,Map<Integer,String>> bigMap = new HashMap<>();//2.遍歷answerList,在遍歷過(guò)程中解析Answer對(duì)象的數(shù)據(jù)存入bigMap for(int i=0;i<answerList.size();i++){Answer answer = answerList.get(i);String uuid = answer.getUuid();Integer questionId = answer.getQuestionId();String context = answer.getAnswerContext();//3.先嘗試從bigMap中獲取smallMap,因?yàn)閍nswer中有很多重復(fù)的uuid//避免重復(fù)創(chuàng)建Map<Integer, String> smallMap = bigMap.get(uuid);if(smallMap==null){//4.smallMap如果為null,說(shuō)明這是此前沒(méi)有創(chuàng)建過(guò)對(duì)應(yīng)的smallMapsmallMap = new HashMap<>();//5.將創(chuàng)建好的smallMap存入bigMap,下次再通過(guò)同樣的uuid獲取就不會(huì)是null了 bigMap.put(uuid, smallMap);}//6.將數(shù)據(jù)存入smallMap smallMap.put(questionId, context);}return bigMap;}

關(guān)鍵點(diǎn):

創(chuàng)建bigMap-->smallMap得到

String context = smallMap.get(questionId); 按照questionList中一一查出的id對(duì)smallMap進(jìn)行取值,從而一一對(duì)應(yīng)

總結(jié):創(chuàng)建 HSSFWorkbook 建表

1).對(duì)每一行進(jìn)行填充,第一行填充題目: 構(gòu)建questionList,list有索引,后面進(jìn)行答案填充時(shí)可以利用索引找到對(duì)應(yīng)的答案 for(Bag bag:bagSet){LinkedHashSet<Question> questionSet = bag.getQuestionSet();//把set轉(zhuǎn)化為L(zhǎng)ist方便索引一一取出questionList.addAll(questionSet);}//填寫(xiě)首行HSSFRow firstRow = sheet.createRow(0);for(int i=0;i<questionList.size();i++){Question question = questionList.get(i); String questionName = question.getQuestionName(); HSSFCell cell = firstRow.createCell(i); cell.setCellValue(questionName); }

2).填充答案

for(int i=0;i<smallMapList.size();i++){//獲取第一個(gè)Map<Integer, String> smallMap = smallMapList.get(i);//viii.這里注意:i控制行索引int rowIndex = i + 1;//ix.根據(jù)rowIndex創(chuàng)建行HSSFRow row = sheet.createRow(rowIndex);//x.創(chuàng)建具體單元格for(int j=0;j<questionList.size();j++){HSSFCell cell = row.createCell(j); //xi.以j為索引從questionList中獲取Question對(duì)象 Question question = questionList.get(j); //xii.從Question對(duì)象中獲取questionId Integer questionId = question.getQuestionId(); //xiii.以questionId為鍵從smallMap中獲取對(duì)應(yīng)的答案內(nèi)容 String context = smallMap.get(questionId); //xiv.用content設(shè)置當(dāng)前單元格內(nèi)容 cell.setCellValue(context); } }

6、使用Spring提供的緩存抽象機(jī)制整合EHCache為項(xiàng)目提供二級(jí)緩存

why?

為了減輕數(shù)據(jù)庫(kù)的負(fù)擔(dān),每次加載調(diào)查問(wèn)卷時(shí)可以進(jìn)行緩存。

適合作為緩存的條件:

1.經(jīng)常查詢

2.可以容忍偶爾的并發(fā)問(wèn)題

3.不會(huì)被其他應(yīng)用修改

Survey項(xiàng)目中適合存入二級(jí)緩存的數(shù)據(jù)

EngageService.PageInfo<Survey> getSurveyPage(Integer userId, boolean completed, Integer pageNum); EngageService.Survey getSurveyDeeply(Integer surveyId); how? 工作原理:偽代碼 try{//1.查詢緩存value = getCache(key);//2.如果緩存不存在if(value==null){//3.查詢數(shù)據(jù)庫(kù)value=dao.select();//4.設(shè)置緩存      setCache(key,value);} //5.返回查詢值return value; } catch(Exception e){} 使用步驟 ①創(chuàng)建鍵生成器類,實(shí)現(xiàn)org.springframework.cache.interceptor.KeyGenerator接口 需要在Spring配置文件中配置對(duì)應(yīng)的bean (也可以采用默認(rèn)的生成方案) ②引入EHCache環(huán)境 [1]加入jar包 [2]引入EHCache自身的配置文件,同時(shí)創(chuàng)建一個(gè)具名的緩存區(qū)域 ③在Spring配置文件中配置緩存抽象對(duì)EHCache的整合 配置EhCacheManagerFactoryBean? 配置EhCacheCacheManager 切面及切面表達(dá)式配置(帥選出需要緩存的方法) <!-- spring 整合ehcache --><!-- 自定義key生成器 --><bean id="userKeyGenerator" class="com.lamsey.survey.Ehcache.UserKeyGenerator"/><!-- 配置 EhCacheManagerFactoryBean工廠--><bean id="ehCacheManagerFactoryBean" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" ><property name="configLocation" value="classpath:ehcache.xml"></property></bean><!-- 配置EhCacheCacheManager --><bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" ><property name="cacheManager" ref="ehCacheManagerFactoryBean"></property></bean><!--切面及切面表達(dá)式配置 --><aop:config><!-- 利用切面表達(dá)式找到切面切入點(diǎn),進(jìn)行切面編程 --><aop:pointcut expression="execution(* *..ResService.getResByServletPath(String)) or execution(* *..AnswerService.getSurveyPage(Integer, boolean, Integer)) or execution(* *..AnswerService.getSurveyDeeply(Integer)) or execution(* *..SurveyService.completedSurvey(Integer))" id="cachePointCut" /><!-- 承上啟下,得到切入點(diǎn),同時(shí)連接處理的方法。對(duì)切入點(diǎn)進(jìn)行處理(cache) --><!-- 緩存切面優(yōu)先級(jí)高于數(shù)據(jù)庫(kù)事務(wù)切面優(yōu)先級(jí) --><aop:advisor advice-ref="cacheAdvice" pointcut-ref="cachePointCut" order="1"/></aop:config> <!-- 對(duì)切入點(diǎn)進(jìn)行處理,這里表現(xiàn)為緩存 --><!-- 這里的自定義key【className.method.param1..paramn】 --><cache:advice id="cacheAdvice" cache-manager="ehCacheCacheManager" key-generator="userKeyGenerator"><!-- 在cache屬性中指定緩存區(qū)域的名稱 --><!-- 指定要使用緩存的具體方法,要求必須是緩存切入點(diǎn)覆蓋范圍內(nèi)的方法 --><cache:caching cache="surveyCache"> <cache:cacheable method=" getResByServletPath" /><cache:cacheable method="getSurveyDeeply"/></cache:caching><!-- 使用另外一個(gè)有可能被清空數(shù)據(jù)的緩存區(qū)域 --><cache:caching cache="surveyCacheEvicable"> <cache:cacheable method="getSurveyPage" /><!-- 執(zhí)行updateSurveyCompleted方法時(shí)清空當(dāng)前緩存區(qū)域 --><!-- 因?yàn)檎{(diào)查有可能更新,當(dāng)更新后就需要進(jìn)行重新獲取參與調(diào)查 ,所以清空該緩存--><cache:cache-evict method="completedSurvey" all-entries="true" /></cache:caching> </cache:advice> 為了減少不必要的事務(wù)操作讓緩存切面的優(yōu)先級(jí)高于事務(wù)切面的優(yōu)先級(jí)。 7.使用石英調(diào)度創(chuàng)建定時(shí)任務(wù)在每月固定時(shí)間自動(dòng)創(chuàng)建日志表,以實(shí)現(xiàn)日志數(shù)據(jù)分流 why? 因?yàn)樾枰脭?shù)據(jù)庫(kù)記錄一些日志信息

因?yàn)槭怯涗浀綌?shù)據(jù)庫(kù),考慮到殺雞不用牛刀。所以采用aop進(jìn)行日志記錄

manager_log表
  • log_id
  • log_operator
  • log_operate_time
  • method_name
  • method_type
  • input_data
  • output_data
  • exception_type
  • exception_message

利用切面來(lái)記錄日志:

環(huán)繞通知,記錄用戶操作信息等(利用ThreadLocal產(chǎn)生request)

環(huán)繞通知:一個(gè)完整的try...catch...finally結(jié)構(gòu) 在切面的通知方法中獲取HttpSession對(duì)象 ①基本思路

?

[1]創(chuàng)建一個(gè)負(fù)責(zé)綁定、移除、獲取request對(duì)象的線程本地化類 [2]在一個(gè)專門(mén)的攔截器中執(zhí)行綁定和移除操作 [3]在切面類的通知方法中獲取前面綁定的request對(duì)象 ③注意事項(xiàng):在RequestBinder類中應(yīng)該以靜態(tài)方式調(diào)用方法,保證local對(duì)象是單例的 /*** 日志記錄儀* @author Administrator**/ @Component @Aspect public class LogRecord {@AutowiredLogService logService;@Around("execution(* *..*Service.update*(..)) || execution(* *..*Service.remove*(..))||execution(* *..*Service.regist(..))||execution(* *..*Service.save*(..)) && !execution(* com.lamsey.survey.component.service.m.LogServiceImpl.*(..))" )public Object recordLog(ProceedingJoinPoint joinPoint){String logOperator=null;String logOperateTime=null,methodName=null,methodType=null,inputData=null,outputData=null,exceptionType=null,exceptionMessage=null;Object returnValue =null;//獲取調(diào)用目標(biāo)方法時(shí)的實(shí)參數(shù)組//調(diào)用目標(biāo)方法try {//獲取目標(biāo)方法簽名Signature signature = joinPoint.getSignature();//簽名中獲取方法類型屬于的類,接口methodType = signature.getDeclaringTypeName();//獲取方法的名字methodName = signature.getName();//輸入的參數(shù)Object[] args = joinPoint.getArgs();if(args.length>0 && args!=null){List<Object> list = Arrays.asList(args);inputData = list.toString();} else{inputData="沒(méi)有輸入的參數(shù)";}// returnValue = joinPoint.proceed(args);// } catch (Throwable e) {//將捕獲到的目標(biāo)方法異常繼續(xù)向上拋出 e.printStackTrace();//異常的類型及信息Throwable cause = e.getCause();if(cause!=null){//獲取異常原因的類型exceptionType = cause.getClass().getName();cause = cause.getCause();} exceptionMessage = e.getMessage(); } finally{//時(shí)間logOperateTime = new SimpleDateFormat("yyyy年MM月dd日hh:mm:ss").format(new Date());//outputValueif(returnValue!=null){outputData = returnValue.toString();} else{outputData ="無(wú)有效的輸出數(shù)據(jù)";}}//收集當(dāng)前登錄的用戶信息//創(chuàng)建TreadLocal,從該變量中當(dāng)前線程上獲取request對(duì)象:獲取sessionHttpServletRequest request = SysContent.getRequest(); HttpSession session = request.getSession();Admin admin=(Admin) session.getAttribute(GlobalNames.LOGIN_ADMIN);User user = (User) session.getAttribute(GlobalNames.LOGIN_USER);String adminPart = (admin==null)?"admin沒(méi)有登陸":admin.getAdminName();String userPart = (user==null)?"user沒(méi)有登陸":user.getUserName();//logOperatorlogOperator = adminPart + "/" + userPart;//將產(chǎn)生的信息存進(jìn)日志數(shù)據(jù)庫(kù)logService.saveLog(new Log(null, logOperator, logOperateTime, methodName, methodType,inputData, outputData, exceptionType, exceptionMessage));//將目標(biāo)方法返回的數(shù)據(jù)繼續(xù)返回給上層調(diào)用的方法return returnValue; } }

?

?

.在IOC容器中配置切面類 ①配置切面類對(duì)應(yīng)的bean ②配置日志切面的切入點(diǎn)表達(dá)式 (execution(* *..*Service.update*(..)) or execution(* *..*Service.remove*(..)) or execution(* *..*Service.regist(..)) or execution(* *..*Service.save*(..))) and !bean(logServiceImpl) ③整體配置方式 <!-- 配置日志切面 --> <bean?id="logRecorder"?class="com.atguigu.survey.log.aspect.LogRecorder"/> <!-- 配置日志切面切入點(diǎn)表達(dá)式 --> <aop:config> <aop:pointcut?expression="(execution(* *..*Service.update*(..)) or execution(* *..*Service.remove*(..)) or execution(* *..*Service.regist(..)) or execution(* *..*Service.save*(..))) and !bean(logServiceImpl)"?id="logPointCut"/> <!-- 配置切面的通知方法 --> <aop:aspect?id="logAspect"?ref="logRecorder"> <aop:around?method="recordLog"?pointcut-ref="logPointCut"/> </aop:aspect> </aop:config> ④無(wú)限死循環(huán)的問(wèn)題 保存日志的方法本身也要記錄日志,從而導(dǎo)致無(wú)限死循環(huán) userService.regist(...) logService.saveLog(...) logService.saveLog(...) logService.saveLog(...) logService.saveLog(...) …… 石英時(shí)鐘創(chuàng)建表: 建表注意事項(xiàng): 1)提前建好未來(lái)三個(gè)月(包括下個(gè)月)的表,當(dāng)月建立來(lái)不及。 2)因?yàn)槿罩颈碓鲩L(zhǎng)太快,所以采用水平分庫(kù),不斷進(jìn)行保存。同時(shí)創(chuàng)立一個(gè)單獨(dú)的數(shù)據(jù)庫(kù)進(jìn)行日志保存。 3)初始部署時(shí)前先創(chuàng)建數(shù)據(jù)表 配置Spring監(jiān)聽(tīng)器在IOC容器啟動(dòng)時(shí)執(zhí)行建表操作,創(chuàng)建當(dāng)月的日志表。 Spring監(jiān)聽(tīng)器在IOC容器啟動(dòng)時(shí)除了建當(dāng)月的表還要建后三個(gè)月的表 定時(shí)任務(wù) ①在固定時(shí)間執(zhí)行固定操作。 ②石英調(diào)度(Quartz)是實(shí)現(xiàn)定時(shí)任務(wù)的其中一種方式。 ③石英調(diào)度和Spring整合思路

?1.)工作bean配置

工作bean:創(chuàng)建Quartz任務(wù)類:繼承org.springframework.scheduling.quartz.QuartzJobBean

?2.)配置石英任務(wù)觸發(fā)器(克龍表達(dá)式)

<property name="cronExpression" value="0 0 0 15 * ? *"></property>

?3.)配置任務(wù)調(diào)度工廠Bean

<!-- 注冊(cè)監(jiān)聽(tīng)器 ,保證一啟動(dòng)就創(chuàng)建三張表--><bean id="createTableListener" class="com.lamsey.survey.log.listener.CreateTableListener"></bean>

?

<!--========= Quartz石英時(shí)鐘====== --><!-- 工作的bean --><bean id="jobDetailBean" class="org.springframework.scheduling.quartz.JobDetailBean"><!--CreateTable的bean由 JobDetailBean創(chuàng)建,不是ioc容器創(chuàng)建,所以logServiceImpl需要注意 --><property name="jobClass" value="com.lamsey.survey.log.quartz.CreateTable" ></property><property name="jobDataMap"><map><!-- 特殊配置:裝配logService --><entry key="logService" value-ref="logServiceImpl"></entry></map></property></bean> <!-- 配置石英任務(wù)觸發(fā)器 --> <bean id="cronTriggerFactoryBean" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"><property name="jobDetail" ref="jobDetailBean"></property><property name="cronExpression" value="0 0 0 15 * ? *"></property></bean><!-- 設(shè)置日程表 --><!-- 配置任務(wù)調(diào)度工廠Bean --><bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="triggers"><list><ref bean="cronTriggerFactoryBean"/></list></property></bean>

?7.使用路由器數(shù)據(jù)源實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作在主數(shù)據(jù)庫(kù)和日志數(shù)據(jù)庫(kù)之間的切換

多數(shù)據(jù)源使用路由器數(shù)據(jù)源管理然后再裝配

?

路由器數(shù)據(jù)源:抽象類AbstractRoutingDataSource

③可以參照的例子 org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter ④細(xì)節(jié)追問(wèn) [1]路由器數(shù)據(jù)源是如何管理那么多具體數(shù)據(jù)源的? Map/鍵值對(duì)形式 [2]路由器數(shù)據(jù)源為什么能夠代替具體的數(shù)據(jù)源裝配給SqlSessionFactoryBean和事務(wù)管理器? SqlSessionFactoryBean和事務(wù)管理器要的數(shù)據(jù)源都是javax.sql.DataSource類型的 所有的路由器數(shù)據(jù)源都必須繼承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

所以要想實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作的切換,需要實(shí)現(xiàn)抽象路由數(shù)據(jù)源

①創(chuàng)建自定義路由器數(shù)據(jù)源類:繼承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource ②實(shí)現(xiàn)抽象方法:determineCurrentLookupKey() 從當(dāng)前線程上獲取key信息 將key信息從線程上移除 將key信息作為返回值返回 ③在Spring配置文件中配置自定義路由器數(shù)據(jù)源 以鍵值對(duì)形式指定所有目標(biāo)數(shù)據(jù)源 指定默認(rèn)數(shù)據(jù)源——在determineCurrentLookupKey()返回null時(shí)使用 ④將自定義路由器數(shù)據(jù)源裝配給SqlSessionFactoryBean和事務(wù)管理器 ⑤在有需要的service方法前執(zhí)行線程綁定:訪問(wèn)日志數(shù)據(jù)庫(kù)的操作 Spring監(jiān)聽(tīng)器建表 石英任務(wù)建表 保存日志信息 分頁(yè)查詢?nèi)罩緮?shù)據(jù) ※注意:因?yàn)槊看斡猛旰髃ey信息需要從線程上移除,所以哪怕是同一個(gè)線程每一個(gè)具體操作前也需要重復(fù)設(shè)置 ※注意:自動(dòng)建表時(shí)的SQL需要參照主數(shù)據(jù)庫(kù)的manager_log或?qū)anager_log復(fù)制到日志數(shù)據(jù)庫(kù) import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import com.lamsey.survey.log.thread.NMRoutingToken; /*** 路由器數(shù)據(jù)源切換實(shí)現(xiàn)* @author Administrator**/ public class NMRoutingDataSource extends AbstractRoutingDataSource{@Overrideprotected Object determineCurrentLookupKey() {//獲取當(dāng)前線程的令牌NMRoutingToken token = NMRoutingToken.getCurrentToken();if (token != null) {String dataSourceName = token.getDataSourceName();//將key從當(dāng)前線程上移除 NMRoutingToken.unbindToken();return dataSourceName;}return null;} }

?

<!--2.配置數(shù)據(jù)源 --><!-- 垂直分庫(kù),log庫(kù)另外存儲(chǔ),所以需要采用路由數(shù)據(jù)源 --><context:property-placeholder location="classpath:dbconfig.properties"/><bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="user" value="${prop.user}"></property><property name="password" value="${prop.password}"></property><property name="jdbcUrl" value="${prop.jdbcUrl}"></property><property name="driverClass" value="${prop.driverClass}"></property></bean><bean id="logDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="user" value="${log.user}"></property><property name="password" value="${log.password}"></property><property name="jdbcUrl" value="${log.jdbcUrl}"></property><property name="driverClass" value="${log.driverClass}"></property></bean><!-- 實(shí)現(xiàn)了抽現(xiàn)類AbstractRoutingDataSource的類 --><bean id="nMRoutingDataSource" class="com.lamsey.survey.log.router.NMRoutingDataSource"><property name="targetDataSources"><map><!-- 當(dāng)輸入log時(shí),調(diào)用 logDataSource數(shù)據(jù)庫(kù)--><entry key="LOG_DATA_SOURCE_KEY" value-ref="logDataSource"></entry></map></property><property name="defaultTargetDataSource" ref="comboPooledDataSource"/> </bean>

?

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/limingxian537423/p/7684718.html

總結(jié)

以上是生活随笔為你收集整理的ssm知识点总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。