SSM框架快速入門javascript
關於SSM框架環境搭建,請點擊這裏前往個人博客:SSM框架整合之環境搭建 因爲本項目採用了maven,關於IDEA搭建maven項目過程請點擊這裏前往個人博客:maven起步前端
項目源碼請 點擊進入 個人GitHub。java
<!--more-->mysql
項目框架:後端:spring+mybatis+springmvc; 前端:bootstrap+Font Awesome圖標集 測試環境:IDEA + tomcat8 + mysql5.7 + jdk8 + maven 數據庫名稱:ssm
1. 實現用戶登陸功能 2. 實現客戶信息的增刪改查功能 3. 實現分頁查詢功能
備註 如上是一個標準的maven項目,咱們來解釋一下各個目錄:jquery
img: 放了一些README.md文檔所須要得圖片,沒有實際意義。git
controller: web層,存放springmvc的相關控制器類。github
mapper: 存放接口和映射文件。由於本項目採用了mybatis的接口開發,因此須要將接口和映射文件放在同一目錄下,而且名稱相同。web
pojo: 存放Java的實體類。ajax
service: 業務層,用於存放一些業務層代碼。算法
不要奇怪爲何沒有出現Dao層,由於本項目相對簡單,並無多複雜的邏輯,因此也就必要再寫一個Dao層進行擴展。
resources: 是maven項目存放配置文件的根目錄,在本例中包含兩個子文件夾:
resource
、spring
。前者是存放一些如logback.properties
的配置文件;後者是存放spring的配置文件(spring和springmvc)。my.sql: 存放了關於項目數據庫建立和表建立的SQL語句。
fonts: 存放一些字體的配置文件。爲了頁面的美感,咱們採用了Awesome圖標集。
lib: 包含了項目中用到的一些前端類庫。
page: 包含全部前端頁面。
繼上一篇博文:Spring MVC起步其實咱們已經瞭解瞭如何整合Spring和Spring MVC框架。那麼,接下來咱們就須要瞭解如何在此基礎上整合Mybatis框架。 首先須知Mybatis框架是一個持久層框架,而Spring MVC是WEB層框架,Spring框架則充當着業務層的角色。那麼將三者聯繫起來正好組成了web--service--dao
的三層架構體系。那麼整合思路就以下所示了:
SQL語句請查看GitHub中resources目錄下的.sql文件 除了建立表,咱們一樣要建立pojo
對象,並提供屬性setter和getter方法。(注意儘可能保持pojo
屬性參數和表字段名稱對應相等)
@RequestMapping(value = "/login") public String login(@RequestParam String username,@RequestParam String password, Model model) { User user = userService.login(username); if (user != null) { if (user.getPassword().equals(password)) { //登陸成功 return "page/page"; } else { model.addAttribute("message", "登陸失敗"); return "page/loginInfo"; } } else { model.addAttribute("message", "你輸入的用戶名或密碼有誤"); return "page/loginInfo"; } }
@RequestMapping
標註login()
方法有兩個做用(前提是必須在XML中開啓註解掃描<context:component-scan/>
):1.表示該方法是請求處理方法;2.指明瞭該方法的請求路徑。@RequestMapping
能夠標記類或方法,分別表示了不一樣層級的請求路徑。例如當前的login()
方法的請求路徑應爲:localhost:8080/xxx/login.do
對於請求體中包含多個參數的狀況,咱們儘可能用@RequestParam
標記參數,以避免出現未知錯誤(但這不是必須的)。 用戶登陸,咱們首先獲取到用戶登陸的用戶名username
和密碼password
,而後根據用戶名查詢並返回,根據此用戶名查詢到的密碼與登陸的密碼進行equals
,若是相等就登陸成功。(固然咱們要判斷根據username
查詢後的返回值是否爲null,不作判斷會產生空指針問題,若是一個空值和另外一個值相比顯然會報錯)。 若是登陸成功,將返回到page/page.jsp
頁面(這是根據咱們在springmvc.xml
下配置的視圖解析器InternalResourceViewResolver
決定的);若是登陸失敗將返回到page/loginInfo.jsp
頁面。
<select id="login" parameterType="String" resultType="User"> select * from user where username = #{username} </select>
咱們使用了Mybatis的接口代理開發模式(保證接口和配置文件在同一目錄下且名稱相同),直接在Mapper.xml
中編寫原生sql語句,便可進行與數據庫間的交互。 id
指明是哪一個方法調用這個sql;parameterType
指定了接口傳遞的參數類型(咱們根據用戶名查詢因此是String類型);resultType
指定該查詢語句返回值的數據類型(由於咱們已經在配置文件啓用了別名配置typeAliases
,因此這裏直接指定pojo對象類名便可;若沒有啓動別名配置,就必須寫類的全限定名)。使用#{}
會將傳遞的參數值會自動添加""
;注意#{}
和${}
區分,後者則是直接拼接傳遞進來的字符串,而不會添加任何符號,且前者能避免sql注入。
所謂添加客戶信息,就是將JSP中提交的表單數據持久化到數據庫中。
建表SQL請看github項目中的resources目錄下的.sql文件 一樣咱們還要建立對應的pojo
,並提供getter和setter方法。(儘可能保持pojo
中的元素屬性名稱和表中字段名稱相同)。
@RequestMapping(value = "/save") public String save(Customer customer, Model model) { customerService.save(customer); model.addAttribute("message", "保存客戶信息系成功"); return "page/info"; }
當點擊了提交按鈕,表單中的全部數據都應該被持久化到數據庫中,而要知道表單中的參數有不少,咱們直接在請求映射方法的參數列表中寫參數顯然是不可取的,那麼咱們若是寫一個pojo對象,Spring就會根據這個pojo對象中的屬性和JSP表單中的參數進行對應,若是徹底對應那麼請求方法會正常執行,一但有某個參數不對應,那麼就會報錯。(注意咱們表單中並不須要指定id
主鍵值,由於設計表時已經指定了該id
主鍵爲自增加,即便不指定值,id
依然會自增,你指定了卻可能會產生錯誤,由於到保證每次的id
值都是遞增的)。當數據持久化成功,就使用Spring的Model
對象在域對象中設置一個名爲message
的值。最後再返回到視圖層。
<insert id="save" parameterType="Customer"> insert into customer( c_name, c_telephone, c_address, c_remark) values( #{c_name}, #{c_telephone}, #{c_address}, #{c_remark} ); </insert>
如上這仍然是普通的SQL語句,注意parameterType
如上咱們設置爲Customer
其實表明的是cn.tycoding.pojo.Customer
這個對象,由於咱們已經在beans.xml
中啓用了mybatis的別名配置。SQL插入語句中不須要指定id
這個字段,緣由是咱們已經配置了id
爲自增主鍵
@RequestMapping(value="/delete") public String delete(@RequestParam int c_id,Model model){ if(customerService.delete(c_id) > 0){ model.addAttribute("message","刪除客戶信息成功"); return "page/info"; }else{ model.addAttribute("message","刪除客戶信息失敗"); return "page/info"; } }
刪除功能只須要根據點擊刪除按鈕時獲取到的id
值,在SQL的delete
語句中where
這個id值,便可以實現根據id刪除客戶信息。
<delete id="delete" parameterType="int"> delete from customer where c_id = #{c_id} </delete>
如此,仍是一個再普通不過的SQL語句,既能夠實現根據id刪除的功能。
更新客戶信息須要咱們實現兩個功能:1.再點擊編輯按鈕時(咱們在按鈕設置了onclick="return edit(${xx.id};"
),如此咱們用js監聽這個事件,點擊了按鈕,js獲取到id,請求後臺根據這個id值查詢數據庫中的數據。那麼咱們先看一下js部分吧:
function edit(id){ $.ajax({ url: 'xxx/findById.do', type: 'POST', dataType: 'json', contentType: 'application/json;charset=UTF-8', data: JSON.stringify({id: id}), success: function(result){ $("#username").val(result.username); $("#password").val(result.password); //繼續講查詢到的字段依次進行賦值... }:: }); }
以上實際是一個ajax請求json格式數據:1.type
指定請求類型;2.dataType
指定了服務器返回數據格式類型;3.contentType
指定發送給服務器的數據格式,默認是application/x-www-form-urlencoded
會使此時的data
參數爲JSON對象,而設置爲application/json
後此時的data
參數就是json字符串了,一樣使用stringify()
也是將data
參數轉換成json字符串。
@ResponseBody @RequestMapping(value="/findById") public Customer findById(@RequestBody Customer customer){ Customer customer_info = customerService.findById(customer.getC_id()); if(customer_info != null){ return customer_info; }else{ return null; } }
@RequestBody
讀取http請求內容,並將數據綁定在映射方法的參數上;@ResponseBody
將映射須要返回的參數轉換成指定的數據格式,而因爲咱們在ajax請求中指定dataType
爲json
,那麼@ReqponseBody
將會返回json格式數據。 當ajax請求了這個映射方法,Controller獲取到指定的id去調用Service層根據這個id查詢數據庫select * from customer where id = #{id}
。而後將數據拼裝成JSON格式,並返回給頁面。最後ajax會走success方法,咱們從返回的數據success:function(result)
中經過result.xx
的方式取出來並經過jquery的val()
方式賦值到指定的位置,那麼就實現了數據回顯。 實現修改功能,首先要知道本來的數據(數據回顯),而後將修改後的數據在此存入到數據庫中(update customer set xx=#{xx} where id = #{id}
。那麼咱們看一下,更新數據庫的Controller:
@RequestMapping(value="/update") public String update(Customer customer,Model model){ int rows = customerService.update(customer); if(rows > 0){ model.addAttribute("message","更新客戶信息成功"); return "page/info"; }else{ model.addAttribute("message","更新客戶信息失敗"); return "page/info"; } }
由於更新數據其實就是簡單的提交表單,表單提交後訪問這個映射方法,而後查詢數據庫,若是正常更新了數據,sql影響行數應該大於0(rows>0),那麼就更新成功,經過SpringMVC的Model方法向request域對象中存入成功信息,在返回的頁面中,經過${message}
EL表達式的方式取出提示信息。 最後咱們看一下更新的SQL如何寫:
<!-- 更新客戶信息的方法 --> <update id="update" parameterType="Customer"> update customer set c_id = #{c_id}, c_name = #{c_name}, c_telephone = #{c_telephone}, c_address = #{c_address}, c_remark = #{c_remark} where c_id = #{c_id} </update>
解釋以前咱們先看一下分頁查詢的頁面:
public class PageBean<T> implements Serializable { //當前頁 private int pageCode; //總頁數=總記錄數/每頁顯示的記錄數 private int totalPage; //總記錄數 private int totalCount; //每頁顯示的記錄數 private int pageSize; //每頁顯示的數據 private List<T> beanList; }
由於咱們要實現分頁查詢,因此沒法避免一些參數,這裏直接將其封裝爲一個JavaBean就是爲了方便調用,而配置自定義泛型<T>
就是爲了供多個對象的調用,若是你在對Customer類進行分頁查詢,那麼在調用時只須要new pageBean<Customer>()
便可將查詢的數據綁定爲Customer
類的數據;對其餘類進行分頁亦是這樣。
pageCode: 表示當前(你點擊)的是第幾頁。
totalPage: 表示總頁數(總頁數=總記錄數/每頁顯示的記錄數)。經過
select count(*) from 表
查詢到總記錄數,每頁顯示的記錄是用戶指定的;那麼總記錄數/每頁顯示幾條記錄就獲得了一共有幾頁(前端頁面展現)。totalCount: 表示總記錄數,由SQL:
select count(*) from 表
查詢到該表咋數據庫中一共多少條記錄數。pageSize: 表示每頁顯示的記錄數,這個是用戶決定的,好比咱們想讓每頁顯示5條數據,那麼這個
pageSize
就應該是5,即每頁會顯示5條記錄。beanList: 表示當前顯示的數據。經上面的一系列查詢和封裝,咱們最終須要將數據返回給頁面,而這些須要返回給頁面的數據最終會被封裝到這個
beanList
中,在jsp中使用<forEach>
標籤遍歷beanList
獲得封裝的數據並顯示到頁面上。
/** * 客戶信息列表(分頁查詢功能) */ @RequestMapping(value="/findByPage") public String findByPage(@RequestParam(value="pageCode",defaultValue = "1",required = false)int pageCode, @RequestParam(value="pageSize",defaultValue = "2",required = false)int pageSize, HttpServletRequest request, Model model){ // 封裝分頁數據 String c_name = request.getParameter("c_name"); String c_telephone = request.getParameter("c_telephone"); Map<String,Object> conMap = new HashMap<String,Object>(); conMap.put("c_name",c_name); conMap.put("c_telephone",c_telephone); // 回顯數據 model.addAttribute("page",customerService.findByPage(pageCode,pageSize,conMap)); return "page/list"; }
對比上面兩張圖,發現,用戶能夠指定的就是pageCode
當前頁和pageSize
每頁顯示的記錄數。因此在點擊按鈕(好比點擊頁碼3
),就會提交表單並訪問Controller的findByPage()
方法。 那麼Controller就須要接受這兩個參數:pageCode
andpageSize
,而且咱們設置:defaultValue
默認值;required
是否必須指定(若是沒有寫false,在每次請求這個方法時就必須指定這個參數的具體值,否則就會報錯)。 方法體中咱們還經過request
域獲取c_name
和c_telephone
(由於要實現條件查詢:輸入信息,查詢數據)。 最後咱們將這些查詢條件封裝到Map集合中,而後調用Service層,將pageCode
和pageSize
以及封裝的查詢條件信息conMap
一同傳入Service層。
首先咱們看一下由Controller調用的Service層接口: 因爲咱們在Controller調用Service的findByPage()
方法時並無給定返回值什麼類型,因此這裏咱們要手動將其修改成PageBean<Customer>
。下面看一下實現類如何編寫:
public PageBean<Customer> findByPage(int pageCode, int pageSize, Map<String, Object> conMap) { HashMap<String,Object> map = new HashMap<String,Object>(); PageBean<Customer> pageBean = new PageBean<Customer>(); //封裝當前頁 pageBean.setPageCode(pageCode); pageBean.setPageSize(pageSize); // 封裝總記錄數(從數據庫中查詢) int totalCount = customerMapper.selectCount(); System.out.println("查詢到的總記錄數:"+totalCount); pageBean.setTotalCount(totalCount); //封裝總頁數 double tc = totalCount; Double num = Math.ceil(tc / pageSize); pageBean.setTotalPage(num.intValue()); // 設置limit分頁查詢的起始位置和終止位置 map.put("start",(pageCode - 1) * pageSize); map.put("size",pageBean.getPageSize()); //封裝每頁顯示的數據 List<Customer> list = customerMapper.findByPage(map); pageBean.setBeanList(list); // 分頁查詢功能也要封裝顯示起始頁和終止頁 conMap.put("start",(pageCode - 1) * pageSize); conMap.put("size",pageBean.getPageSize()); // 封裝 List<Customer> listCondition = customerMapper.findCondition(conMap); pageBean.setBeanList(listCondition); return pageBean; }
做爲業務層,固然負責梳理業務信息,首先咱們須要將Controller傳入進來的pageCode
和pageSize
封裝進PageBean
的相關屬性中。而後查詢總記錄數(經過select count(*) from 表
查詢獲得),根據總記錄數pageCount
和前臺傳入的pageSize
每頁顯示的記錄數計算獲得總頁數,一樣封裝到PageBean
中,最後咱們要設置分頁的起始位置start
和數量size
,調用Mapper查詢數據庫中的數據,將數據封裝到beanList
中便可。可是要注意咱們其實寫了兩個分頁查詢的方法:findByPage()
和findCondition()
由於二者都須要分頁展現到頁面上。最後解釋兩點:
start
爲*(當前頁-1)此時每頁顯示的記錄數*。 設置size
爲咱們在pageBean中封裝的每頁顯示幾條記錄數。 例如:咱們目前頁面每頁顯示2條數據,點擊下一頁,則顯示的數據就是第3 - 5
條。<!-- 查詢總的記錄數 --> <select id="selectCount" resultType="int"> select count(*) from customer; </select> <!-- 分頁查詢 --> <select id="findByPage" parameterType="Map" resultMap="BaseResultMap"> select * from customer <if test="start != null and size != null"> limit #{start},#{size} </if> </select> <!-- 多條件查詢 --> <select id="findCondition" parameterType="Map" resultMap="BaseResultMap"> <!-- where 1=1 能夠保證where後的語句永遠是正確的 由於在where後的動態SQL可能會執行也可能不會不會執行,若是沒有執行,那麼where後將跟一個空值,那麼顯然這樣是會報錯的 --> select * from customer where 1 = 1 <if test="c_name != null and c_name != ''"> and c_name like concat('%', #{c_name}, '%') </if> <if test="c_telephone != null and c_telephone != ''"> and c_telephone like concat('%', #{c_telephone}, '%') </if> <!-- 咱們經過在Service中的計算決定了咱們每次請求的數據應該從那一頁開始,那一頁結束 --> <if test="start != null and size != null"> limit #{start},#{size} </if> </select>
注意幾點:
首先咱們看一下頁碼是如何展現出來的:
百度分頁算法(每頁顯示10個頁碼): 當點擊頁碼7以後的頁碼,最前端的頁碼依次減小 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] 點擊[7] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] 算法: 若 總頁數 <= 10 則begin=1 end=總頁數 若 總頁數 > 10 則begin=當前頁-5 end=當前頁+4 頭溢出: 若begin < 1 則begin=1 end=10 尾溢出: 若end > 總記錄數 則brgin=end-9 end=總頁數 此項目設置每頁顯示5個頁碼: 若 總頁數 <= 5 則begin=1 end=總頁數 若 總頁數 > 5 則begin=當前頁-1 end=當前頁+3 頭溢出: 若begin < 1 則begin=1 end=5 尾溢出: 若end > 總記錄數 則brgin=end-4 end=總頁數 * (end表示頁面的最後一個頁碼,begin表示頁面的第一個頁碼)
以前有人會問道這個頭溢出和尾溢出是什麼意思?其實仔細看看,若是咱們安裝上面設計的算法邏輯:頭溢出就是指當頁數不少一直點擊上一頁,爲避免出現第0頁而設置的;那麼尾溢出就以下圖所示狀況了:
到此爲止,咱們基本講完了SSM框整合的過程,你是否看明白了呢?其實整合SSM框架並不難,按照這個思路,咱們學習完SSM框架整合,就能夠着手練習一些小項目了。詳細過程,你們能夠從個人項目源碼中分析。
<br/>
若是你們有興趣,歡迎你們加入個人Java交流羣:671017003 ,一塊兒交流學習Java技術。博主目前一直在自學JAVA中,技術有限,若是能夠,會盡力給你們提供一些幫助,或是一些學習方法,固然羣裏的大佬都會積極給新手答疑的。因此,別猶豫,快來加入咱們吧!
<br/>
If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.