本系統後臺使用SSM框架編寫,前臺頁面使用當前主流的Bootstrap和jQuery框架完成頁面信息展現功能(關於Bootstrap的知識,有興趣的讀者可參考黑馬程序員編著的《響應式Web開發項目教程》)。系統中主要實現了兩大功能模塊:用戶登陸模塊和客戶管理模塊,這兩個模塊的主要功能如圖18-1所示。css
本系統根據功能的不一樣,項目結構能夠劃分爲如下幾個層次。html
· 持久對象層(也稱持久層或持久化層):該層由若干持久化類(實體類)組成。前端
· 數據訪問層(DAO層):該層由若干DAO接口和MyBatis映射文件組成。接口的名稱統一以Dao結尾,且MyBatis的映射文件名稱要與接口的名稱相同。java
· 業務邏輯層(Service層):該層由若干Service接口和實現類組成。在本系統中,業務邏輯層的接口統一使用Service結尾,其實現類名稱統一在接口名後加Impl。該層主要用於實現系統的業務邏輯。mysql
· Web表現層:該層主要包括Spring MVC中的Controller類和JSP頁面。Controller類主要負責攔截用戶請求,並調用業務邏輯層中相應組件的業務邏輯方法來處理用戶請求,而後將相應的結果返回給JSP頁面。爲了讓讀者更清晰地瞭解各個層次之間的關係,下面經過一張圖來描述各個層次的關係和做用,如圖18-2所示。程序員
在正式講解項目的編寫以前,先來了解項目中所涉及的包文件、配置文件以及頁面文件等在項目中的組織結構,如圖18-3所示。web
BOOT客戶管理系統開發環境以下。ajax
· 操做系統:Windowsspring
· Web服務器:Tomcat 8.0sql
· Java開發包:JDK8
· 開發工具:Eclipse Java EE IDE for Web Developers
· 數據庫:MySQL 5.5
· 瀏覽器:火狐或IE 8.0以上版本
本系統中主要涉及用戶登陸和客戶管理功能,所以在系統中會涉及系統用戶表和客戶信息表。除此以外,客戶信息中的客戶來源和所屬行業等內容是根據數據字典表中的信息查詢出的,因此還會涉及一個數據字典表。這3張表的表結構如表18-一、表18-2和表18-3所示。
因爲本系統使用的是SSM框架開發,所以須要準備這三大框架的JAR包。除此以外,項目中還涉及數據庫鏈接、JSTL標籤等,因此還要準備其餘JAR包。整個系統所須要準備的JAR共計35個,具體以下所示。
1.Spring框架所需的JAR包(10個)
主要包括4個核心模塊JAR, AOP開發使用的JAR, JDBC和事務的JAR。
· aopalliance-1.0.jar
· aspectjweaver-1.8.10.jar
· spring-aop-4.3.6.RELEASE.jar
· spring-aspects-4.3.6.RELEASE.jar
· spring-beans-4.3.6.RELEASE.jar
· spring-context-4.3.6.RELEASE.jar
· spring-core-4.3.6.RELEASE.jar
· spring-expression-4.3.6.RELEASE.jar
· spring-jdbc-4.3.6.RELEASE.jar
· spring-tx-4.3.6.RELEASE.jar
2.Spring MVC框架所須要的JAR包(2個)
· spring-web-4.3.6.RELEASE.jar
· spring-webmvc-4.3.6.RELEASE.jar
3.MyBatis框架所需的JAR包(13個)主要包括核心包mybatis-3.4.2.jar,以及其解壓文件夾中lib目錄下的全部JAR。
· ant-1.9.6.jar
· ant-launcher-1.9.6.jar
· asm-5.1.jar· cglib-3.2.4.jar
· commons-logging-1.2.jar
· javassist-3.21.0-GA.jar
· log4j-1.2.17.jar
· log4j-api-2.3.jar
· log4j-core-2.3.jar
· mybatis-3.4.2.jar
· ognl-3.1.12.jar
· slf4j-api-1.7.22.jar
· slf4j-log4j12-1.7.22.jar
4.MyBatis與Spring整合的中間JAR(1個)
· mybatis-spring-1.3.1.jar
5.數據庫驅動JAR包(1個)
· mysql-connector-java-5.1.40-bin.jar
6.數據源dbcp所需JAR包(2個)
· commons-dbcp2-2.1.1.jar
· commons-pool2-2.4.2.jar
7.JSTL標籤庫JAR包(2個)
· taglibs-standard-impl-1.2.5.jar
· taglibs-standard-spec-1.2.5.jar
8.Jackson框架所需JAR包(3個)
· jackson-annotations-2.8.6.jar
· jackson-core-2.8.6.jar
· jackson-databind-2.8.6.jar
9.Java工具類JAR(1個)
· commons-lang3-3.4.jar
上面所須要準備的JAR包(除JSTL的2個JAR包和commons-lang3-3.4.jar外),都是本書前面章節所使用過的。讀者在學習本章時,能夠直接下載項目源碼,並使用源碼中的JAR包。
小提示
本書中使用的JSTL版本是1.2.5,此版本中須要引入的JAR包爲taglibs-standard-spec-1.2.5.jar(至關於以前的jstl.jar,屬於接口定義類)和taglibs-standard-impl-1.2.5.jar jar(至關於以前的standard.jar,屬於實現類)。這兩個JAR包能夠經過網址「http://tomcat.apache.org/download-taglibs.cgi#Standard-1.2.5」下載獲得。
經過MySQL 5.5 Command Line Client登陸數據庫後,建立一個名稱爲boot_crm的數據庫,並選擇該數據庫。經過SQL命令將本書資源中所提供的boot_crm.sql文件導入到boot_crm數據庫中,便可導入本系統所使用的所有數據,其具體實現SQL命令以下。
(1)建立數據庫。
create database boot_crm;
(2)選擇所建立的數據庫。
use boot_crm;
(3)導入數據庫文件,這裏假設該文件在F盤的根目錄下,其導入命令以下。
source F:\boot_crm.sql;
除此以外,還能夠經過其餘客戶端軟件導入sql文件,如SQLyog等。
建立crm數據庫,執行sql
在Eclipse中,建立一個名稱爲boot-crm的Web項目,將系統所準備的所有JAR包複製到項目的lib目錄中,併發布到類路徑下。
(1)在項目目錄下建立一個源文件夾config,並在config文件夾下分別建立數據庫常量配置文件、Spring配置文件、MyBatis配置文件、log4j配置文件、資源配置文件以及Spring MVC配置文件。其中log4j配置文件log4j.properties、數據庫常量配置文件jdbc.properties與MyBatis配置文件mybatis-config.xml的配置與第17章講解整合時的配置代碼基本相同(注意修改數據庫名稱與包名),這裏將再也不重複講解。其餘3個配置文件的代碼分別如文件18-一、文件18-2和文件18-3所示。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 別名 --> <typeAlises> <package name="com.itheima.core.pojo"/> </typeAlises> </configuration>
配置數據庫信息
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/crm?characterEncoding=utf-8 jdbc.username=root jdbc.password=root
配置日誌信息
# Global logging configuration log4j.rootLogger=DEBUG, stdout # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
須要配置:
加載properties文件,數據源,SqlSessionFactory,Mapper掃描
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <!-- 配置 讀取properties文件 jdbc.properties --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置 數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <!-- 數據庫驅動 --> <property name="driverClassName" value="${jdbc.driver}" /> <!-- 鏈接數據庫的url --> <property name="url" value="${jdbc.url}" /> <!-- 鏈接數據庫的用戶名 --> <property name="username" value="${jdbc.username}" /> <!-- 鏈接數據庫的密碼 --> <property name="password" value="${jdbc.password}" /> </bean> <!-- 配置Mybatis的工廠 SqlSessionFactory --> <bean class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 設置MyBatis核心配置文件所在位置 --> <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" /> <!-- 設置數據源 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 數據源 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 傳播行爲 --> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="create*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="select*" propagation="SUPPORTS" read-only="true" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> <tx:method name="query*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <!-- 切面 --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.crm.service.*.*(..))" /> </aop:config> <!-- 配置Mapper掃描 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 設置Mapper掃描包 --> <property name="basePackage" value="com.itheima.crm.mapper" /> </bean> <!-- 配置Service掃描 --> <context:component-scan base-package="com.itheima.crm.service" /> </beans>
上述代碼與上一章整合時的配置文件代碼有所不一樣的是增長了事務傳播行爲以及切面的配置。在事務的傳播行爲中,只有查詢方法的事務爲只讀,添加、修改和刪除的操做必須歸入事務管理。
#客戶來源 CUSTOMER_FROM_TYPE=002 #客戶行業 CUSTOMER_INDUSTRY_TYPE=001 #客戶級別 CUSTOMER_LEVEL_TYPE=006
上述配置代碼分別表示客戶來源、所屬行業和客戶級別,其值對應的是數據字典表中dict_type_code字段的值。
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 配置Controller掃描 --> <context:component-scan base-package="com.itheima.crm.controller" /> <!-- 加載屬性文件 controller須要的配置信息 --> <context:property-placeholder location="classpath:resource.properties" /> <!-- 配置註解驅動:處理器映射器和適配器--> <mvc:annotation-driven /> <!-- 對靜態資源放行,此配置中的文件,將不被前端控制器攔截--> <mvc:resources location="/css/" mapping="/css/**"/> <mvc:resources location="/js/" mapping="/js/**"/> <mvc:resources location="/fonts/" mapping="/fonts/**"/> <!-- 另一種方式 解決靜態資源沒法被springMVC處理的問題 --> <mvc:default-servlet-handler /> <!-- 配置視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前綴 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 後綴 --> <property name="suffix" value=".jsp" /> </bean> </beans>
上述代碼除配置了須要掃描的包、註解驅動和視圖解析器外,還增長了加載屬性文件和訪問靜態資源的配置。
(2)在web.xml中,配置Spring的監聽器、編碼過濾器和SpringMVC的前端控制器等信息,如文件18-4所示。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>ssm</display-name> <!-- 系統默認頁面--> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 配置spring --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <!-- 配置監聽器加載spring --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置編碼過濾器,解決post的亂碼問題 --> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置SpringMVC前端核心控制器 --> <servlet> <servlet-name>crm</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> <!-- 配置springmvc何時啓動,參數必須爲整數 --> <!-- 若是爲0或者大於0,則springMVC隨着容器啓動而啓動 --> <!-- 若是小於0,則在第一次請求進來的時候啓動 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>crm</servlet-name> <!-- 全部的請求都進入springMVC --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
3.引入頁面資源將項目運行所須要的CSS文件、字體、圖片、JS、自定義標籤文件和JSP文件按照圖18-3中的結構引入到項目中。
至此,開發系統前的環境準備工做就已經完成。此時若是將項目發佈到Tomcat服務器並訪問項目首頁地址http://localhost:8080/boot-crm/index.jsp,如圖18-4所示。
從圖18-4能夠看出,訪問系統首頁時,頁面所展現的是系統登陸頁面。在下一節中,咱們將對系統的登陸功能編寫進行詳細講解。
BOOT客戶管理系統用戶登陸功能的實現流程如圖18-5所示。
從圖18-5能夠看出,用戶登陸過程當中首先要驗證用戶名和密碼是否正確,若是正確,能夠成功登陸系統,系統會自動跳轉到主頁;若是錯誤,則在登陸頁面給出錯誤提示信息。
下面就依照圖18-5中的流程,來實現系統登陸功能,具體步驟以下。
在src目錄下,建立一個com.itheima.crm. pojo包,在包中建立用戶持久化類User,並在User類中定義用戶相關屬性以及相應的getter/setter方法,如文件18-5所示。
文件18-5 User.java
package com.itheima.core.pojo; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private Integer user_id; // 用戶id private String user_code; // 用戶帳戶 private String user_name; // 用戶名稱 private String use_password; // 用戶密碼 // getter/setter方法 }
(1)建立用戶DAO層接口。在src目錄下,建立一個com.itheima.crm.dao包,在包中建立一個用戶接口UserDao,並在接口中編寫經過帳號和密碼查詢用戶的方法,如文件18-6所示。
文件18-6 UserDao.java
public interface UserDao { /** * 經過帳戶和密碼查詢用戶 * @param usercode * @param password * @return */ public User findUser(@Param("usercode") String usercode,@Param("password")String password); }
在上述方法代碼的參數中,@Param("usercode")表示爲參數usercode命名,命名後,在映射文件的SQL中,使用#{usercode}就能夠獲取usercode的參數值。
(2)建立映射文件。在com.itheima.core.dao包中,建立一個MyBatis映射文件UserDao.xml,並在映射文件中編寫查詢用戶信息的執行語句,如文件18-7所示。
<?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.itheima.core.dao.UserDao"> <!-- 查詢用戶 --> <select id="findUser" parameterType="String" resultType="User"> select * from sys_user where user_code = #{usercode} and user_password = #{password} and user_state = '1' </select> </mapper>
上述代碼經過映射查詢語句來查詢系統用戶表中的可用用戶。
(1)建立用戶Service層接口。在src目錄下,建立一個com.itheima.core.service包,在包中建立UserService接口,並在該接口中編寫一個經過帳號和密碼查詢用戶的方法,如文件18-8所示。
文件18-8 UserService.java
public interface UserService { /** * 經過帳號和密碼查詢用戶 * @param usercode * @param password * @return */ public User findUser(String usercode,String password); }
(2)建立用戶Service層接口的實現類。在src目錄下,建立一個com.itheima.core.service. impl包,並在包中建立UserService接口的實現類UserServiceImpl,在類中編輯並實現接口中的方法,如文件18-9所示。
文件18-9 UserServiceImpl.java
@Service @Transactional public class UserServiceImpl implements UserService { // 注入Userdao @Autowired private UserDao userdao; @Override public User findUser(String usercode, String password) { User user=this.userdao.findUser(usercode, password); return user; } }
在上述代碼的findUser()方法中,調用了UserDao對象中的findUser()方法來查詢用戶信息,並將查詢到的信息返回。
在src目錄下,建立一個com.itheima.core.web.controller包,在包中建立用戶控制器類UserController,編輯後的代碼如文件18-10所示。
文件18-10 UserController.java
public class UserController { @Autowired private UserService userService; /** * 用戶登陸 * @param usercode * @param password * @param model * @param session * @return */ @RequestMapping(value="/login",method=RequestMethod.POST) public String login(String usercode,String password,Model model,HttpSession session) { User user= userService.findUser(usercode,password); if(user!=null) { //將用戶對象添加到Session session.setAttribute("USER_SESSION", user); // 跳轉到主頁面 return "customer"; } model.addAttribute("msg", "帳號或密碼錯誤,請從新登陸!"); // 返回到登陸頁面 return "login"; } }
在文件18-10中,首先經過@Autowired註解將UserService對象注入到了本類中,而後建立了一個用於用戶登陸的login()方法。因爲在用戶登陸時,表單都會以POST方式提交,因此將@RequestMapping註解的method屬性值設置爲RequestMethod.POST。在login()方法中,首先經過頁面中傳遞過來的帳號和密碼查詢用戶,而後經過if語句判斷是否存在該用戶。若是存在,就將用戶信息存儲到Session中,並跳轉到系統主頁面;若是不存在,則提示錯誤信息,並返回到登陸頁面。
(1)系統默認首頁index.jsp主要實現了一個轉發功能,在訪問時會轉發到登陸頁面,其實現代碼如文件18-11所示。
文件18-11 index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <!-- 轉發到登陸頁面 --> <jsp:forward page="/WEB-INF/jsp/login.jsp"></jsp:forward> </body> </html>
(2)登陸頁面中,主要包含一個登陸表單,其頁面實現代碼如文件18-12所示。
文件18-12 login.jsp
在文件18-12中,核心代碼是用戶登陸操做的form表單,該表單在提交時會經過check()方法檢查帳戶或密碼是否爲空,若是爲空,則經過標籤提示用戶「帳號或密碼不能爲空!」;若是帳號和密碼都已填寫,則將表單提交到以「/login.action」結尾的請求中。
將項目發佈到Tomcat服務器並啓動,成功訪問登陸頁面後,便可輸入帳號和密碼登陸系統。在執行登陸操做以前,先查看一下數據庫中sys_user表中的數據,如圖18-6所示。
從圖18-6能夠看出,表sys_user中包含4個帳號(user_code)以m開頭的用戶信息。此時在登陸頁面中輸入帳號「m0001」和密碼「123」,單擊「登陸」按鈕後,瀏覽器的顯示結果如圖18-7所示。
從圖18-7能夠看出,系統已經成功進入客戶管理頁面,這說明系統登陸成功。此時因爲項目中並無實現客戶查詢功能,因此客戶列表中沒有任何數據。
雖然在18.4.1節中已經實現了用戶登陸功能,可是此功能還並不完善。假設在其餘控制器類中也包含一個訪問客戶管理頁面的方法,那麼用戶徹底能夠繞過登陸步驟,而直接經過訪問該方法的方式進入客戶管理頁面。爲了驗證上述內容,咱們能夠在用戶控制器類UserController中編寫一個跳轉到客戶管理頁面的方法,其代碼以下所示。
/** * 模擬其餘類中跳轉到客戶管理頁面的方法 * @return */ @RequestMapping(value="/toCustomer.action") public String toCustomer() { return "customer"; }
此時,若是經過瀏覽器訪問地址http://localhost:8080/boot-crm/toCustomer.action,瀏覽器就會直接顯示客戶管理頁面
顯然,讓未登陸的用戶直接訪問到客戶管理頁面,是十分不安全的。爲了不此種狀況的發生,並提高系統的安全性,咱們能夠建立一個登陸攔截器來攔截全部請求。只有已登陸用戶的請求才可以經過,而對於未登陸用戶的請求,系統會將請求轉發到登陸頁面,並提示用戶登陸,其執行流程如圖18-9所示。
實現用戶登陸驗證的具體過程以下。
在src目錄下,建立一個com.itheima.core.interceptor包,並在包中建立登陸攔截器類LoginInterceptor,來實現用戶登陸的攔截功能,編輯後如文件18-13所示。
文件18-13 LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 獲取請求的URL String url=request.getRequestURI(); // URL:除了登陸請求外,其餘的URL都進行攔截控制 if(url.indexOf("/login.action")>=0) { return true; } // 獲取session HttpSession session = request.getSession(); User user = (User)session.getAttribute("USER_SESSION"); // 判斷Session中是否有用戶數據,若是有,則返回true,繼續向下執行 if(user!=null) { return true; } // 不符合條件的給出提示信息,並轉發到登陸頁面 request.setAttribute("msg", "您尚未登陸,請先登陸!"); request.getRequestDispatcher("/WEB_INF/jsp/login.jsp").forward(request, response); return false; } }
在文件18-13的preHandle()方法中,首先獲取了用戶URL請求,而後經過請求來判斷是否爲用戶登陸操做,只有對用戶登陸的請求才不進行攔截。接下來獲取了Session對象,並獲取Session中的用戶信息。若是Session中的用戶信息不爲空,則表示用戶已經登陸,攔截器將放行;若是Session中的用戶信息爲空,則表示用戶未登陸,系統會轉發到登陸頁面,並提示用戶登陸。
在springmvc-config.xml文件中,配置登陸攔截器信息,其配置代碼以下。
<!-- 配置攔截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.itheima.core.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
上述配置代碼會將全部的用戶請求都交由登陸攔截器來處理。至此,登陸攔截器的實現工做就已經完成。
發佈項目並啓動Tomcat服務器後,再次經過瀏覽器訪問地址http://localhost:8080/boot-crm/toCustomer.action時,瀏覽器的顯示結果如圖18-10所示。
從圖18-10能夠看出,未登陸的用戶在執行訪問客戶管理頁面方法後,並無成功跳轉到客戶管理頁面,而是轉發到了系統登陸頁面,同時在頁面的登陸窗口中也給出了提示信息。這也就說明用戶登陸驗證功能已成功實現。
用戶登陸模塊中還包含一個功能——退出登陸。成功登陸後的用戶會跳轉到客戶管理頁面,而且在頁面中會顯示已登陸的用戶名稱,如圖18-11所示。
從圖18-11能夠看出,頁面的右上角中已經顯示了登陸用戶「小韓」,而且彈出列表框最下方爲「退出登陸」。那麼要如何實現「退出登陸」功能呢?
在customer.jsp頁面中,圖18-11中彈出列表框的實現代碼以下。
<ul class="dropdown-menu dropdown-user"> <li><a href="#"><i class="fa fa-user fa-fw"></i> 用戶:${USER_SESSION.user_name} </a> </li> <li><a href="#"><i class="fa fa-gear fa-fw"></i> 系統設置</a></li> <li class="divider"></li> <li> <a href="${pageContext.request.contextPath }/logout.action"> <i class="fa fa-sign-out fa-fw"></i>退出登陸 </a> </li> </ul>
從上述代碼中能夠看出,顯示的登陸用戶名稱是經過EL表達式從Session中獲取的,而單擊「退出登陸」連接時,會提交一個以「/logout.action」結尾的請求。爲了完成退出登陸功能,咱們須要在用戶控制器類中編寫一個退出登陸的方法。在方法執行時,須要清除Session中的用戶信息,而且在退出登陸後,系統要返回到登陸頁面。所以,須要在用戶控制器類UserController中編寫退出登陸和返回到登陸頁面的方法,這兩個方法的實現代碼以下。
/** * 退出登陸 * @param session * @return */ @RequestMapping(value="/logout.action") public String logout(HttpSession session) { // 清除Session session.invalidate(); // 重定向到登陸頁面的跳轉方法 return "redirect:login.action"; } /** * 向用戶登陸頁面跳轉 * @return */ @RequestMapping(value="/logout.action",method= RequestMethod.GET) public String tologin() { return "login"; }
至此,「退出登陸」的功能代碼就已經編寫完成。重啓項目並登陸系統後,單擊圖18-11中的「退出登陸」便可退出系統。
客戶管理模塊是本系統的核心模塊,該模塊中實現了對客戶的查詢、添加、修改和刪除功能。在接下來的幾個小節中,將對這幾個功能的實現進行詳細講解。
在實際應用中,不管是企業級項目,仍是互聯網項目,使用最多的必定是查詢操做。不論是在列表中展現全部數據的操做,仍是對單個數據的修改或者刪除操做,都須要先查詢並展現出數據庫中的數據。
查詢操做一般能夠分爲按條件查詢和查詢全部,但在實際使用時,咱們能夠將這兩種查詢編寫在一個方法中使用,即當有條件時,就按照條件查詢;當沒有條件時,就查詢全部。同時,因爲數據庫中的數據可能有不少,若是讓這些數據在一個頁面中所有顯示出來,勢必會使頁面數據的可讀性變得不好,因此咱們還須要考慮將這些數據進行分頁查詢顯示。
綜合上述分析以及客戶頁面的顯示功能,BOOT客戶管理系統的查詢功能須要實現的功能如圖18-12所示。
從圖18-12能夠看出,客戶管理模塊中的查詢可分爲按照條件查詢和分頁查詢,這兩種查詢操做所查詢出的數據都會顯示在客戶信息列表中。若是未選擇任何條件,那麼客戶信息列表將分頁查詢顯示出全部數據。咱們要如何實現客戶的條件查詢和分頁查詢呢?下面將對客戶管理中的查詢功能實現進行詳細講解,具體步驟以下。
分析:
根據分析,DAO須要編寫兩個方法:
在com.itheima.core.pojo包中,建立客戶持久化類、數據字典持久化類、查詢條件包裝類,編輯後如文件18-14和文件18-15所示。
文件18-14 Customer.java
public class Customer implements Serializable{ private static final long serialVersionUID = 1L; private Long cust_id;// 客戶編號 private String cust_name;// 客戶名稱 private Long cust_user_id;// 負責人id private Long cust_create_id;// 建立人id private String cust_source;// 客戶信息來源 private String cust_industry;// 客戶所屬行業 private String cust_level;// 客戶級別 private String cust_linkman;// 聯繫人 private String cust_phone;// 固定電話 private String cust_mobile;// 移動電話 private String cust_zipcode;// 郵政編碼 private String cust_address;// 聯繫地址 private Date cust_createtime;// 建立時間 //getter setter方法 }
在文件18-14中,聲明瞭與客戶數據表對應的屬性並定義了各個屬性的getter/setter方法。
文件18-15 BaseDict.java
public class BaseDict implements Serializable { private static final long serialVersionUID = 1L; private String dict_id;// 數據字典id private String dict_type_code;// 數據字典類別代碼 private String dict_type_name;// 數據字典類別名稱 private String dict_item_name; // 數據字典項目名稱 private String dict_item_code;// 數據字典項目代碼 private Integer dict_sort;// 排序字段 private String dict_enable;// 是否可用 private String dict_memo;// 備註 //getter setter方法 }
在文件18-15中,聲明瞭與數據字典表對應的屬性並定義了各個屬性的getter/setter方法。
QueryVo.java
public class QueryVo { private String custName;// 客戶名稱 private String custSource; // 客戶來源 private String custIndustry;// 所屬行業 private String custLevel;// 客戶級別 private Integer page = 1;// 當前頁碼數 默認查詢第1頁 private Integer start;// 起始行 數據庫從哪一條數據開始查 private Integer rows = 10; // 所取行數 每頁顯示數據條數 //getter setter方法 }
受請求參數的QueryVo,裏面包含查詢條件屬性和分頁數據。須要注意的是,屬性中的star和rows用於執行分頁操做,其中start表示分頁操做中的起始行,而rows則表示分頁中所選取的行數。
(1)建立客戶DAO層接口和映射文件。
在com.itheima.core.dao包中,建立一個CustomerDao接口,並在接口中編寫查詢客戶列表和客戶總數的方法,而後建立一個與接口同名的映射文件,如文件18-16和文件18-17所示。
文件18-16 CustomerDao.java
public interface CustomerDao { /** * 根據queryVo分頁查詢數據 * * @param queryVo * @return */ List<Customer> queryCustomerByQueryVo(QueryVo queryVo); /** * 根據queryVo查詢數據條數 * * @param queryVo * @return */ Integer queryCountByQueryVo(QueryVo queryVo); }
文件18-17 CustomerDao.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.itheima.core.dao.CustomerDao"> <!-- SQL片斷--> <sql id="customerQueryVo"> <where> <if test="custName != null and custName != ''"> AND a.cust_name LIKE '%${custName}%' </if> <if test="custSource != null and custSource != ''"> AND a.cust_source = #{custSource} </if> <if test="custIndustry != null and custIndustry != ''"> AND a.cust_industry = #{custIndustry} </if> <if test="custLevel != null and custLevel != ''"> AND a.cust_level = #{custLevel} </if> </where> </sql> <!-- 根據queryVo分頁查詢數據 --> <select id="queryCustomerByQueryVo" parameterType="com.itheima.core.pojo.QueryVo" resultType="com.itheima.core.pojo.Customer"> SELECT a.cust_id, a.cust_name, a.cust_user_id, a.cust_create_id, b.dict_item_name cust_source, c.dict_item_name cust_industry, d.dict_item_name cust_level, a.cust_linkman, a.cust_phone, a.cust_mobile, a.cust_zipcode, a.cust_address, a.cust_createtime FROM customer a LEFT JOIN base_dict b ON a.cust_source = b.dict_id LEFT JOIN base_dict c ON a.cust_industry = c.dict_id LEFT JOIN base_dict d ON a.cust_level = d.dict_id <include refid="customerQueryVo" /> <!-- 執行分頁查詢--> <if test="start != null"> LIMIT #{start}, #{rows} </if> </select> <!-- 根據queryVo查詢數據條數 --> <select id="queryCountByQueryVo" parameterType="com.itheima.core.pojo.QueryVo" resultType="integer"> SELECT count(1) FROM customer a <include refid="customerQueryVo" /> </select> </mapper>
在文件18-17中,首先編寫了一個SQL片斷來做爲映射查詢客戶信息的條件,而後編寫了查詢全部客戶的映射查詢方法。在方法的SQL中,分別經過左外鏈接的方式從數據字典表base_dict中的類別代碼字段查詢出了相應的類別信息,同時經過limit來實現數據的分頁查詢。最後編寫了一個查詢客戶總數的映射查詢語句用於分頁使用。
(2)建立數據字典DAO層接口和映射文件。
在com.itheima.core.dao包中,建立一個BaseDictDao接口,並在接口中編寫根據類別代碼查詢數據字典的方法,而後建立一個與接口同名的映射文件,如文件18-18和文件18-19所示
文件18-18 BaseDictDao.java
public interface BaseDictDao { /** * 根據類別代碼查詢數據 * @param dictTypecode * @return */ public List<BaseDict> queryBaseDictByDictTypeCode(String dictTypecode); }
文件18-19 BaseDictDao.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.itheima.core.dao.BaseDictDao"> <!-- 根據類別代碼查詢數據 --> <select id="queryBaseDictByDictTypeCode" parameterType="String" resultType="com.itheima.core.pojo.BaseDict"> SELECT * FROM base_dict WHERE dict_type_code = #{dict_type_code} </select> </mapper>
(1)引入分頁標籤類。在src目錄下,建立一個com.itheima.common.utils包,在包中引入分頁時使用的標籤類文件Page.java和NavigationTag.java,這兩個文件可直接從源代碼中獲取,其具體實現代碼如文件18-20和文件18-21所示。
文件18-20 Page.java
public class Page<T> { private int total;// 總條數 private int page; // 當前頁 private int size; // 每頁數 private List<T> rows; // 結果集 // getter setter 方法 }
文件18-21 NavigationTag.java
/** * 顯示格式:首頁 上一頁 1 2 3 4 5下一頁 尾頁 */ public class NavigationTag extends TagSupport { static final long serialVersionUID = 2372405317744358833L; /** * request 中用於保存Page<E> 對象的變量名,默認爲「page」 */ private String bean = "page"; /** * 分頁跳轉的url地址,此屬性必須 */ private String url = null; /** * 顯示頁碼數量 */ private int number = 5; @Override public int doStartTag() throws JspException { JspWriter writer = pageContext.getOut(); HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); Page page = (Page) request.getAttribute(bean); if (page == null) return SKIP_BODY; url = resolveUrl(url, pageContext); try { // 計算總頁數 int pageCount = page.getTotal() / page.getSize(); if (page.getTotal() % page.getSize() > 0) { pageCount++; } writer.print("<nav><ul class=\"pagination\">"); //首頁連接路徑 String homeUrl = append(url, "page", 1); //末頁連接路徑 String backUrl = append(url, "page", pageCount); // 顯示「上一頁」按鈕 if (page.getPage() > 1) { String preUrl = append(url, "page", page.getPage() - 1); preUrl = append(preUrl, "rows", page.getSize()); writer.print("<li><a href=\"" + homeUrl + "\">" + "首頁</a></li>"); writer.print("<li><a href=\"" + preUrl + "\">" + "上一頁</a></li>"); } else { writer.print("<li class=\"disabled\"><a href=\"#\">" + "首頁 </a></li>"); writer.print("<li class=\"disabled\"><a href=\"#\">" + "上一頁 </a></li>"); } // 顯示當前頁碼的前2頁碼和後兩頁碼 // 若1 則 1 2 3 4 5, 若2 則 1 2 3 4 5, 若3 則1 2 3 4 5, // 若4 則 2 3 4 5 6 ,若10 則 8 9 10 11 12 int indexPage =1; if(page.getPage() - 2 <=0){ indexPage=1; }else if(pageCount-page.getPage() <=2){ indexPage=pageCount-4; }else{ indexPage= page.getPage() - 2; } for (int i= 1;i <= number && indexPage <= pageCount;indexPage++,i++){ if (indexPage == page.getPage()) { writer.print("<li class=\"active\"><a href=\"#\">" + indexPage +"<spanclass=\"sr-only\"></span></a></li>"); continue; } String pageUrl = append(url, "page", indexPage); pageUrl = append(pageUrl, "rows", page.getSize()); writer.print("<li><a href=\"" + pageUrl + "\">" + indexPage + "</a></li>"); } // 顯示「下一頁」按鈕 if (page.getPage() < pageCount) { String nextUrl = append(url, "page", page.getPage() + 1); nextUrl = append(nextUrl, "rows", page.getSize()); writer.print("<li><a href=\"" + nextUrl + "\">" + "下一頁</a></li>"); writer.print("<li><a href=\"" + backUrl + "\">" + "尾頁</a></li>"); } else { writer.print("<li class=\"disabled\"><a href=\"#\">" + "下一頁</a></li>"); writer.print("<li class=\"disabled\"><a href=\"#\">" + "尾頁</a></li>"); } writer.print("</nav>"); } catch (IOException e) { e.printStackTrace(); } return SKIP_BODY; } private String append(String url, String key, int value) { return append(url, key, String.valueOf(value)); } /** * 爲url 參加參數對兒 */ private String append(String url, String key, String value) { if (url == null || url.trim().length() == 0) { return ""; } if (url.indexOf("?") == -1) { url = url + "?" + key + "=" + value; } else { if (url.endsWith("?")) { url = url + key + "=" + value; } else { url = url + "&" + key + "=" + value; } } return url; } /** * 爲url 添加翻頁請求參數 */ private String resolveUrl(String url, javax.servlet.jsp.PageContext pageContext) throws JspException { Map params = pageContext.getRequest().getParameterMap(); for (Object key : params.keySet()) { if ("page".equals(key) || "rows".equals(key)){ continue; } Object value = params.get(key); if (value == null){ continue; } if (value.getClass().isArray()) { url = append(url, key.toString(), ((String[]) value)[0]); } else if (value instanceof String) { url = append(url, key.toString(), value.toString()); } } return url; } public String getBean() { return bean; } public void setBean(String bean) { this.bean = bean; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public void setNumber(int number) { this.number = number; } }
(2)建立數據字典及客戶的Service層接口。在com.itheima.core.service包中建立一個名稱爲BaseDictService和CustomerService的接口,編輯後如文件18-22和文件18-23所示。
文件18-22 BaseDictService.java
public interface BaseDictService { /** * 根據類別代碼查詢 * * @param dictTypeCode * @return */ List<BaseDict> queryBaseDictByDictTypeCode(String dictTypeCode); }
文件18-23 CustomerService.java
public interface CustomerService { /** * 根據條件分頁查詢客戶 * * @param queryVo * @return */ Page<Customer> queryCustomerByQueryVo(QueryVo queryVo); }
3)建立數據字典及客戶Service層接口的實現類。在com.itheima.core.service.impl包中分別建立數據字典和客戶Service層接口的實現類BaseDictServiceImpl和CustomerServiceImpl,編輯後的代碼如文件18-24和文件18-25所示。
文件18-24 BaseDictServiceImpl.java
@Service public class BaseDictServiceImpl implements BaseDictService{ @Autowired private BaseDictDao baseDictDao; @Override public List<BaseDict> queryBaseDictByDictTypeCode(String dictTypeCode) { return baseDictDao.queryBaseDictByDictTypeCode(dictTypeCode); } }
文件18-23 CustomerServiceImpl.java
@Service public class CustomerServiceImpl implements CustomerService { @Autowired private CustomerDao customerDao; @Override public Page<Customer> queryCustomerByQueryVo(QueryVo queryVo) { // 判斷參數對象 if(null != queryVo) { if(StringUtils.isNotBlank(queryVo.getCustName())) { queryVo.setCustName(queryVo.getCustName()); } if(StringUtils.isNotBlank(queryVo.getCustSource())) { queryVo.setCustSource(queryVo.getCustSource()); } if(StringUtils.isNotBlank(queryVo.getCustIndustry())) { queryVo.setCustIndustry(queryVo.getCustIndustry()); } if(StringUtils.isNotBlank(queryVo.getCustLevel())) { queryVo.setCustLevel(queryVo.getCustLevel()); } if(StringUtils.isNotBlank(queryVo.getCustName())) { queryVo.setCustName(queryVo.getCustName()); } if(StringUtils.isNotBlank(queryVo.getCustName())) { queryVo.setCustName(queryVo.getCustName()); } } // 設置查詢條件,從哪一條數據開始查 page和rows有初始值 queryVo.setStart((queryVo.getPage() - 1) * queryVo.getRows()); // 查詢數據結果集 List<Customer> list = this.customerDao.queryCustomerByQueryVo(queryVo); // 查詢到的數據總條數 int total = this.customerDao.queryCountByQueryVo(queryVo); // 封裝返回的page對象 Page<Customer> page = new Page<Customer>(); page.setPage(queryVo.getPage());// 當前頁 和參數同樣 page.setRows(list);// 結果集 page.setSize(queryVo.getRows());// 每頁數 和參數同樣 page.setTotal(total);// 總條數 return page; }
在文件18-25的實現方法中,首先判斷參數是否爲空,而後判斷條件查詢中的客戶名稱、信息來源、所屬行業和客戶級別是否爲空,只有不爲空時,才添加到參數對象中。接下來獲取了頁面傳遞過來的當前頁page和每頁數信息rows,由此獲得起始行start。而後查詢全部的客戶信息以及客戶總數。最後將查詢出的全部信息封裝到Page對象中並返回。
在com.itheima.core.controller包中,建立客戶控制器類CustomerController,編輯後如文件18-26所示。
文件18-26 CustomerController.java
@Controller public class CustomerController { // 客戶來源 @Value("${CUSTOMER_FROM_TYPE}") private String CUSTOMER_FROM_TYPE; // 客戶行業 @Value("${CUSTOMER_INDUSTRY_TYPE}") private String CUSTOMER_INDUSTRY_TYPE; // 客戶級別 @Value("${CUSTOMER_LEVEL_TYPE}") private String CUSTOMER_LEVEL_TYPE; @Autowired private BaseDictService baseDictService; @Autowired private CustomerService customerService; /** * 顯示用戶列表 * @return */ @RequestMapping(value="/customer/list") public String list(Model model,QueryVo queryVo) { // 已在tomcat的server.xml中修改了uri的編碼爲UTF-8,此處代碼可不寫 <Connector URIEncoding="UTF-8" /> // try { // // 解決get請求亂碼問題 // if (StringUtils.isNotBlank(queryVo.getCustName())) { // queryVo.setCustName(new String(queryVo.getCustName().getBytes("ISO-8859-1"), "UTF-8")); // } // } catch (Exception e) { // e.printStackTrace(); // } // 客戶來源 List<BaseDict> fromType = baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_FROM_TYPE); // 所屬行業 List<BaseDict> industryType =baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_INDUSTRY_TYPE); // 客戶級別 List<BaseDict> levelType = baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_LEVEL_TYPE); // 把前端頁面須要顯示的數據放到模型中 model.addAttribute("fromType", fromType); model.addAttribute("industryType", industryType); model.addAttribute("levelType", levelType); // 條件、分頁查詢數據 Page<Customer> page = this.customerService.queryCustomerByQueryVo(queryVo); // 把分頁查詢的結果放到模型中 model.addAttribute("page", page); // 數據回顯 model.addAttribute("custName", queryVo.getCustName()); model.addAttribute("custSource", queryVo.getCustSource()); model.addAttribute("custIndustry", queryVo.getCustIndustry()); model.addAttribute("custLevel", queryVo.getCustLevel()); return "customer"; }
在客戶控制器類中,首先聲明瞭customerService和baseDictService屬性,並經過@Autowired註解將這兩個對象注入到本類中;而後分別定義了客戶來源、所屬行業和客戶級別屬性,並經過@Value註解將resource.properties文件中的屬性值賦給這3個屬性;最後編寫了查詢客戶列表的方法來執行查詢操做,其中第1個參數page的默認值爲1,表示從第1條開始,第2個參數的默認值爲10,表示每頁顯示10條數據。
(1)在18.3.3小節準備項目環境時,已經說明了須要引入自定義標籤文件。在本項目中,自定義標籤文件主要用於實現分頁功能,其標籤名稱爲commons.tld,標籤中的實現代碼如文件18-27所示。
文件18-27 commons.tld
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <!-- 指定標籤庫的版本號 --> <tlib-version>2.0</tlib-version> <!-- 指定JSP的版本號 --> <jsp-version>1.2</jsp-version> <!-- 指定標籤庫的名稱 --> <short-name>common</short-name> <!-- 指定標籤庫的URI --> <uri>http://itheima.com/common/</uri> <!-- 指定標籤庫的顯示名稱 --> <display-name>Common Tag</display-name> <!-- 指定標籤庫的描述 --> <description>Common Tag library</description> <!-- 註冊一個自定義標籤 --> <tag> <!-- 指定註冊的自定義標籤名稱 --> <name>page</name> <!-- 指定自定義標籤的標籤處理器類 --> <tag-class>com.itheima.common.utils.NavigationTag</tag-class> <!-- 指定標籤體類型 --> <body-content>JSP</body-content> <!-- 描述 --> <description>create navigation for paging</description> <!-- 指定標籤中的屬性 --> <attribute> <!-- 指定屬性名稱 --> <name>url</name> <!-- 該屬性爲true時表示其指定是屬性爲必須屬性 --> <required>true</required> <!-- 該屬性用於指定能不能使用表達式來動態指定數據,爲true時表示能夠 --> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>bean</name> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>number</name> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>
在文件18-27中,第13行代碼就是咱們在使用自定義標籤時引入的URI,第23行代碼指定了自定義標籤的處理器類,其餘內容參見代碼註釋信息。小提示在實際開發時,分頁功能一般都會使用通用的工具類,或分頁組件來實現,而這些工具類和組件通常不須要開發人員本身編寫,只需學會使用便可。因此本書中的分頁工具類和上面的分頁標籤文件讀者只需直接引入,而且掌握如何使用,而不須要本身編寫。
(2)在customer.jsp中,編寫條件查詢和顯示客戶列表以及分頁查詢的代碼,具體如文件18-28所示。
文件18-28 customer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page trimDirectiveWhitespaces="true"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="itheima" uri="http://itcast.cn/common/"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>客戶管理-BootCRM</title> <!-- 引入css樣式文件 --> <!-- Bootstrap Core CSS --> <link href="<%=basePath%>css/bootstrap.min.css" rel="stylesheet" /> <!-- MetisMenu CSS --> <link href="<%=basePath%>css/metisMenu.min.css" rel="stylesheet" /> <!-- DataTables CSS --> <link href="<%=basePath%>css/dataTables.bootstrap.css" rel="stylesheet" /> <!-- Custom CSS --> <link href="<%=basePath%>css/sb-admin-2.css" rel="stylesheet" /> <!-- Custom Fonts --> <link href="<%=basePath%>css/font-awesome.min.css" rel="stylesheet" type="text/css" /> <link href="<%=basePath%>css/boot-crm.css" rel="stylesheet" type="text/css" /> </head> <body> ... <!-- 客戶列表查詢部分 start--> <div id="page-wrapper"> <div class="row"> <div class="col-lg-12"> <h1 class="page-header">客戶管理</h1> </div> <!-- /.col-lg-12 --> </div> <!-- /.row --> <div class="panel panel-default"> <div class="panel-body"> <form class="form-inline" method="get" action="${pageContext.request.contextPath }/customer/list.action"> <div class="form-group"> <label for="customerName">客戶名稱</label> <input type="text" class="form-control" id="customerName" value="${custName }" name="custName" /> </div> <div class="form-group"> <label for="customerFrom">客戶來源</label> <select class="form-control" id="customerFrom" name="custSource"> <option value="">--請選擇--</option> <c:forEach items="${fromType}" var="item"> <option value="${item.dict_id}" <c:if test="${item.dict_id == custSource}">selected</c:if>> ${item.dict_item_name } </option> </c:forEach> </select> </div> <div class="form-group"> <label for="custIndustry">所屬行業</label> <select class="form-control" id="custIndustry" name="custIndustry"> <option value="">--請選擇--</option> <c:forEach items="${industryType}" var="item"> <option value="${item.dict_id}" <c:if test="${item.dict_id == custIndustry}"> selected</c:if>> ${item.dict_item_name } </option> </c:forEach> </select> </div> <div class="form-group"> <label for="custLevel">客戶級別</label> <select class="form-control" id="custLevel" name="custLevel"> <option value="">--請選擇--</option> <c:forEach items="${levelType}" var="item"> <option value="${item.dict_id}" <c:if test="${item.dict_id == custLevel}"> selected</c:if>> ${item.dict_item_name } </option> </c:forEach> </select> </div> <button type="submit" class="btn btn-primary">查詢</button> </form> </div> </div> <a href="#" class="btn btn-primary" data-toggle="modal" data-target="#newCustomerDialog" onclick="clearCustomer()">新建</a> <div class="row"> <div class="col-lg-12"> <div class="panel panel-default"> <div class="panel-heading">客戶信息列表</div> <!-- /.panel-heading --> <table class="table table-bordered table-striped"> <thead> <tr> <th>編號</th> <th>客戶名稱</th> <th>客戶來源</th> <th>客戶所屬行業</th> <th>客戶級別</th> <th>固定電話</th> <th>手機</th> <th>操做</th> </tr> </thead> <tbody> <c:forEach items="${page.rows}" var="row"> <tr> <td>${row.cust_id}</td> <td>${row.cust_name}</td> <td>${row.cust_source}</td> <td>${row.cust_industry}</td> <td>${row.cust_level}</td> <td>${row.cust_phone}</td> <td>${row.cust_mobile}</td> <td> <a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#customerEditDialog" onclick= "editCustomer(${row.cust_id})">修改</a> <a href="#" class="btn btn-danger btn-xs" onclick="deleteCustomer(${row.cust_id})">刪除</a> </td> </tr> </c:forEach> </tbody> </table> <div class="col-md-12 text-right"> <itheima:page url="${pageContext.request.contextPath }/customer/list.action" /> </div> <!-- /.panel-body --> </div> <!-- /.panel --> </div> <!-- /.col-lg-12 --> </div> </div> <!-- 客戶列表查詢部分 end--> </div> ...
在上述頁面代碼中,第4行和第5行代碼引入了JSTL標籤和自定義的分頁標籤;第17~62行代碼是一個條件查詢的form表單,單擊「查詢」按鈕後,會提交到一個「list.action」請求中;第72~105行代碼是顯示客戶信息列表的表格,查詢出的客戶信息會在此表格中顯示;第107~108行代碼是自定義的分頁標籤,該標籤會根據客戶數以及設定的頁數數據顯示內容。
發佈項目並啓動Tomcat服務器後,進入客戶管理頁面,而後單擊「查詢」按鈕便可查詢出全部客戶信息,而且這些信息都已分頁顯示,如圖18-13所示。
細心的讀者必定會發現,在進入客戶管理頁面時,客戶信息列表是沒有任何顯示的,只有單擊「查詢」按鈕後,纔會顯示出數據。那麼有什麼辦法可以讓其進入該頁面時就默認顯示數據呢?
要實現登陸後展現客戶信息列表的操做很簡單,只需將用戶控制器類(UserController)中用戶登陸方法(login())內跳轉到主頁面的語句修改成重定向到主頁的跳轉方法便可,修改後的語句以下。
@RequestMapping(value="/login",method=RequestMethod.POST) public String login(String usercode,String password,Model model,HttpSession session) { User user= userService.findUser(usercode,password); if(user!=null) { //將用戶對象添加到Session session.setAttribute("USER_SESSION", user); // 重定向到主頁,直接顯示出分頁後的客戶列表信息 // return "customer"; return "redirect:customer/list.action"; } model.addAttribute("msg", "帳號或密碼錯誤,請從新登陸!"); // 返回到登陸頁面 return "login"; }
這樣,登陸成功後,客戶管理頁面將會直接顯示出分頁後的客戶列表信息。
在本系統中,添加客戶的操做是經過頁面彈出窗口實現的,當單擊「新建」按鈕時,將彈出「新建客戶信息」窗口,如圖18-16所示。
填寫完圖18-16中的全部客戶信息後,單擊「建立客戶」按鈕,將執行添加客戶的操做。那麼此操做具體是如何實現的呢?下面將對系統中的添加客戶的功能實現進行詳細講解,具體步驟以下。1.實現頁面功能代碼在頁面中,「新建」按鈕連接的實現代碼以下。
在頁面中,「新建」按鈕連接的實現代碼以下。
<a href="#" class="btn btn-primary" data-toggle="modal" data-target="#newCustomerDialog" onclick="clearCustomer()">新建</a>
在上述代碼中,data-toggle="modal" 和data-target="#newCustomerDialog"是Bootstrap的模態框代碼,當單擊「新建」按鈕後,會彈出id爲newCustomerDialog的窗口,同時經過onclick屬性執行clearCustomer()方法來清除窗口中的全部數據。在customer.jsp中,新建客戶模態框的顯示代碼如文件18-29所示。
<!-- 建立客戶模態框 --> <div class="modal fade" id="newCustomerDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h4 class="modal-title" id="myModalLabel">新建客戶信息</h4> </div> <div class="modal-body"> <form class="form-horizontal" id="new_customer_form"> <div class="form-group"> <label for="new_customerName" class="col-sm-2 control-label"> 客戶名稱 </label> <div class="col-sm-10"> <input type="text" class="form-control" id="new_customerName" placeholder="客戶名稱" name="cust_name" /> </div> </div> <div class="form-group"> <label for="new_customerFrom" style="float:left;padding:7px 15px 0 27px;">客戶來源</label> <div class="col-sm-10"> <select class="form-control" id="new_customerFrom" name="cust_source"> <option value="">--請選擇--</option> <c:forEach items="${fromType}" var="item"> <option value="${item.dict_id}"<c:if test="${item.dict_id == custSource}">selected</c:if>> ${item.dict_item_name } </option> </c:forEach> </select> </div> </div> <div class="form-group"> <label for="new_custIndustry" style="float:left;padding:7px 15px 0 27px;">所屬行業</label> <div class="col-sm-10"> <select class="form-control" id="new_custIndustry" name="cust_industry"> <option value="">--請選擇--</option> <c:forEach items="${industryType}" var="item"> <option value="${item.dict_id}"<c:if test="${item.dict_id == custIndustry}"> selected</c:if>> ${item.dict_item_name } </option> </c:forEach> </select> </div> </div> <div class="form-group"> <label for="new_custLevel" style="float:left;padding:7px 15px 0 27px;">客戶級別</label> <div class="col-sm-10"> <select class="form-control" id="new_custLevel" name="cust_level"> <option value="">--請選擇--</option> <c:forEach items="${levelType}" var="item"> <option value="${item.dict_id}"<c:if test="${item.dict_id == custLevel}"> selected</c:if>>${item.dict_item_name }</option> </c:forEach> </select> </div> </div> <div class="form-group"> <label for="new_linkMan" class="col-sm-2 control-label">聯繫人</label> <div class="col-sm-10"> <input type="text" class="form-control" id="new_linkMan" placeholder="聯繫人" name="cust_linkman" /> </div> </div> <div class="form-group"> <label for="new_phone" class="col-sm-2 control-label">固定電話</label> <div class="col-sm-10"> <input type="text" class="form-control" id="new_phone" placeholder="固定電話" name="cust_phone" /> </div> </div> <div class="form-group"> <label for="new_mobile" class="col-sm-2 control-label">移動電話</label> <div class="col-sm-10"> <input type="text" class="form-control" id="new_mobile" placeholder="移動電話" name="cust_mobile" /> </div> </div> <div class="form-group"> <label for="new_zipcode" class="col-sm-2 control-label">郵政編碼</label> <div class="col-sm-10"> <input type="text" class="form-control" id="new_zipcode" placeholder="郵政編碼" name="cust_zipcode" /> </div> </div> <div class="form-group"> <label for="new_address" class="col-sm-2 control-label">聯繫地址</label> <div class="col-sm-10"> <input type="text" class="form-control" id="new_address" placeholder="聯繫地址" name="cust_address" /> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">關閉</button> <button type="button" class="btn btn-primary" onclick="createCustomer()">建立客戶</button> </div> </div> </div> </div>
在上述代碼中,第15~121行代碼的form表單中的實現代碼,即爲用戶所須要填寫的客戶信息。
爲保證每次單擊「新建」按鈕後所彈出的模態框內沒有任何數據,須要在頁面中建立一個clearCustomer()方法來清空模態框中的內容,clearCustomer()方法的實現代碼以下所示。
//清空新建客戶窗口中的數據 function clearCustomer() { $("#new_customerName").val(""); $("#new_customerFrom").val("") $("#new_custIndustry").val("") $("#new_custLevel").val("") $("#new_linkMan").val(""); $("#new_phone").val(""); $("#new_mobile").val(""); $("#new_zipcode").val(""); $("#new_address").val(""); }
填寫完模態框中的信息後,單擊「建立客戶」按鈕,會執行createCustomer()方法,該方法的實現代碼以下。
// 建立客戶 function createCustomer() { $.post("<%=basePath%>customer/create.action", $("#new_customer_form").serialize(),function(data){ if(data =="OK"){ alert("客戶建立成功!"); window.location.reload(); }else{ alert("客戶建立失敗!"); window.location.reload(); } }); }
createCustomer()方法會經過jQuery Ajax的POST請求將id爲new_customer_form的表單序列化,而後提交到以「/create.action」結尾的請求中,若是其返回值爲「OK」則表示客戶建立成功,不然建立客戶失敗。
在客戶控制器類CustomerController中編寫建立客戶的方法,其代碼以下所示。
/** * 建立客戶 * @param customer * @param session * @return */ @RequestMapping("/customer/create.action") @ResponseBody public String customerCreate(Customer customer,HttpSession session) { // 獲取Session中的當前用戶信息 User user=(User)session.getAttribute("USER_SESSION"); // 將當前用戶id存儲在客戶對象中 customer.setCust_create_id(user.getUser_id()); // 建立Date對象 Date date = new Date(); // 獲得一個Timestamp格式的時間,存入mysql中的時間格式「yyyy/MM/dd HH:mm:ss」 // Timestamp timeStamp = new Timestamp(date.getTime()); customer.setCust_createtime(date); // 執行Service層中的建立方法,返回的是受影響的行數 int rows = customerService.createCustomer(customer); if(rows >0) { return "ok"; }else { return "FAIL"; } }
在上述方法代碼中,首先獲取了Session中的當前用戶信息,而後將當前用戶id信息添加到Customer對象的建立人id屬性中。接下來建立了Date對象,並將格式化的時間信息添加到Customer對象的cust_createtime屬性中。最後執行Service層中的createCustomer()方法,其返回值爲數據庫中受影響的行數,若是其值大於0,則表示建立成功,返回「OK」字符串信息,不然返回「FAIL」字符串。
通常在異步獲取數據時使用。在使用@RequestMapping註解後,方法的返回值一般會被解析爲跳轉路徑(如某個頁面或某個方法),而加上@Responsebody註解後,其返回結果將不會被解析爲跳轉路徑,而是將經過HttpMessageConverter轉換爲指定格式後的結果(如json、xml等)直接寫入HTTP Response對象的body中,這樣頁面中的方法就能夠獲取其返回值。
(1)建立接口方法。在CustomerService接口中,建立一個createCustomer()方法,其代碼以下所示。
/** * 建立客戶 * @param customer * @return */ int createCustomer(Customer customer);
(2)建立實現類方法。在CustomerServiceImpl中,實現createCustomer()方法,編輯後的代碼以下所示。
@Override public int createCustomer(Customer customer) { return customerDao.createCustomer(customer); }
(1)建立接口方法。在CustomerDao中,編寫建立客戶的方法,其代碼以下所示。
/** * 建立客戶 * @param customer * @return */ int createCustomer(Customer customer);
(2)建立映射插入語句。在CustomerDao.xml中,編寫執行插入操做的映射插入語句,其代碼以下所示。
<!-- 添加客戶 --> <insert id="createCustomer" parameterType="com.itheima.core.pojo.Customer"> insert into customer( cust_name, cust_user_id, cust_create_id, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile, cust_zipcode, cust_address, cust_createtime ) values(#{cust_name}), #{cust_user_id}, #{cust_create_id}, #{cust_source}, #{cust_industry}, #{cust_level}, #{cust_linkman}, #{cust_phone}, #{cust_mobile}, #{cust_zipcode}, #{cust_address}, #{cust_createtime} ) </insert>
5.添加客戶測試至此,添加客戶的實現代碼就已經編寫完成。發佈並啓動項目後,進入客戶管理頁面,單擊「新建」按鈕,並填寫新建客戶信息,如圖18-17所示。
單擊圖18-17中的「建立客戶」按鈕後,若是程序正確執行,則會彈出「客戶建立成功!」的彈出窗口,再次單擊「肯定」後,瀏覽器就會刷新當前頁面。要查詢所建立的客戶是否已建立成功很是簡單,只須要在條件查詢中查找客戶名稱爲「小程」的客戶,如圖18-18所示。
從圖18-18能夠看出,新建立的客戶「小程」的信息已被正確查詢出。至此添加客戶的功能就已經成功實現。
修改操做與添加操做同樣,也是經過頁面彈出窗口實現的,當單擊頁面對應數據的「修改」按鈕時,將彈出「修改客戶信息」窗口,如圖18-19所示。
從圖18-19能夠看出,修改客戶信息窗口與新建客戶信息窗口的顯示內容基本相同,但修改客戶信息窗口中回顯出了須要修改的客戶信息。當修改客戶信息後,單擊「保存修改」按鈕,便可執行修改操做。下面就對本系統中修改客戶的功能實現進行詳細講解,具體步驟以下。
在頁面中,「修改」按鈕連接的實現代碼以下。
<a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#customerEditDialog" onclick= "editCustomer(${row.cust_id})">修改</a>
與新建方法同樣,當單擊「修改」按鈕後,會彈出id爲customerEditDialog的模態窗口,同時經過執行onclick屬性的editCustomer()方法來獲取須要修改客戶的全部數據。在customer.jsp中,修改客戶模態框的顯示代碼如文件18-30所示。文件18-30 customer.jsp
<!-- 修改客戶模態框 --> <div class="modal fade" id="customerEditDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h4 class="modal-title" id="myModalLabel">修改客戶信息</h4> </div> <div class="modal-body"> <form class="form-horizontal" id="edit_customer_form"> <input type="hidden" id="edit_cust_id" name="cust_id"/> <div class="form-group"> <label for="edit_customerName" class="col-sm-2 control-label">客戶名稱</label> <div class="col-sm-10"> <input type="text" class="form-control" id="edit_customerName" placeholder="客戶名稱" name="cust_name" /> </div> </div> <div class="form-group"> <label for="edit_customerFrom" style="float:left;padding:7px 15px 0 27px;">客戶來源</label> <div class="col-sm-10"> <select class="form-control" id="edit_customerFrom" name="cust_source"> <option value="">--請選擇--</option> <c:forEach items="${fromType}" var="item"> <option value="${item.dict_id}"<c:if test="${item.dict_id == custSource}"> selected</c:if>>${item.dict_item_name }</option> </c:forEach> </select> </div> </div> <div class="form-group"> <label for="edit_custIndustry" style="float:left;padding:7px 15px 0 27px;">所屬行業</label> <div class="col-sm-10"> <select class="form-control" id="edit_custIndustry" name="cust_industry"> <option value="">--請選擇--</option> <c:forEach items="${industryType}" var="item"> <option value="${item.dict_id}"<c:if test="${item.dict_id == custIndustry}"> selected</c:if>>${item.dict_item_name }</option> </c:forEach> </select> </div> </div> <div class="form-group"> <label for="edit_custLevel" style="float:left;padding:7px 15px 0 27px;">客戶級別</label> <div class="col-sm-10"> <select class="form-control" id="edit_custLevel" name="cust_level"> <option value="">--請選擇--</option> <c:forEach items="${levelType}" var="item"> <option value="${item.dict_id}"<c:if test="${item.dict_id == custLevel}"> selected</c:if>>${item.dict_item_name }</option> </c:forEach> </select> </div> </div> <div class="form-group"> <label for="edit_linkMan" class="col-sm-2 control-label">聯繫人</label> <div class="col-sm-10"> <input type="text" class="form-control" id="edit_linkMan" placeholder="聯繫人" name="cust_linkman" /> </div> </div> <div class="form-group"> <label for="edit_phone" class="col-sm-2 control-label">固定電話</label> <div class="col-sm-10"> <input type="text" class="form-control" id="edit_phone" placeholder="固定電話" name="cust_phone" /> </div> </div> <div class="form-group"> <label for="edit_mobile" class="col-sm-2 control-label">移動電話</label> <div class="col-sm-10"> <input type="text" class="form-control" id="edit_mobile" placeholder="移動電話" name="cust_mobile" /> </div> </div> <div class="form-group"> <label for="edit_zipcode" class="col-sm-2 control-label">郵政編碼</label> <div class="col-sm-10"> <input type="text" class="form-control" id="edit_zipcode" placeholder="郵政編碼" name="cust_zipcode" /> </div> </div> <div class="form-group"> <label for="edit_address" class="col-sm-2 control-label">聯繫地址</label> <div class="col-sm-10"> <input type="text" class="form-control" id="edit_address" placeholder="聯繫地址" name="cust_address" /> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">關閉</button> <button type="button" class="btn btn-primary" onclick="updateCustomer()">保存修改</button> </div> </div> </div> </div>
在上述代碼中,第15~125行代碼中的form表單就是修改客戶信息的實現代碼。因爲在修改客戶信息時,須要先獲取到該客戶的全部信息,並顯示到修改信息的窗口內,因此須要在頁面中編寫一個獲取客戶信息的方法editCustomer(),該方法的實現代碼以下所示。
// 經過id獲取修改的客戶信息 function editCustomer(id) { $.ajax({ type:"get", url:"<%=basePath%>customer/queryCustomerById.action", data:{"id":id}, success:function(data) { $("#edit_cust_id").val(data.cust_id); $("#edit_customerName").val(data.cust_name); $("#edit_customerFrom").val(data.cust_source) $("#edit_custIndustry").val(data.cust_industry) $("#edit_custLevel").val(data.cust_level) $("#edit_linkMan").val(data.cust_linkman); $("#edit_phone").val(data.cust_phone); $("#edit_mobile").val(data.cust_mobile); $("#edit_zipcode").val(data.cust_zipcode); $("#edit_address").val(data.cust_address); } }); }
上述方法代碼使用了jQuery Ajax的方式來獲取所須要修改的客戶信息,獲取成功後,會將該客戶信息添加到修改客戶模態框中的相應位置。
在CustomerController類中,編寫經過id獲取客戶信息和更新客戶的方法,其代碼以下所示。
/** * 根據id查詢客戶,返回json格式數據 * * @param id * @return */ @RequestMapping("/customer/getCustomerById.action") @ResponseBody public Customer queryCustomerById(Long id) { Customer customer = this.customerService.queryCustomerById(id); return customer; } /** * 更新客戶 * * @param id * @return */ @RequestMapping("/customer/update") @ResponseBody public String updateCustomerById(Customer customer) { int rows = this.customerService.updateCustomerById(customer); if(rows >0) { return "ok"; }else { return "FAIL"; } }
上述兩個方法中,都只是調用了Service層中的相應方法,並將相應的執行結果返回。
(1)建立接口方法。
在CustomerService接口中,建立經過id獲取客戶信息和更新客戶的方法,代碼以下所示。
/** * 根據id編輯客戶數據 * * @param customer * @return */ int updateCustomerById(Customer customer); /** * 根據id刪除客戶 * * @param id */ void deleteCustomerById(Long id);
(2)建立實現類方法。
在CustomerServiceImpl中,實現接口中的方法,編輯後的代碼以下所示。
@Override public Customer queryCustomerById(Long id) { Customer customer = this.customerDao.queryCustomerById(id); return customer; } @Override public int updateCustomerById(Customer customer) { return customerDao.updateCustomerById(customer); }
在上述代碼中,getCustomerById()方法返回的是所須要修改的客戶對象,而updateCustomer()方法執行後,返回的是數據庫中受影響的行數。
(1)建立接口方法。
在CustomerDao接口中,編寫經過id獲取客戶信息和更新客戶的方法,其代碼以下所示。
/** * 根據id查詢用戶 * * @param id * @return */ Customer queryCustomerById(Long id); /** * 根據id編輯客戶數據 * * @param customer * @return */ int updateCustomerById(Customer customer);
(2)建立映射語句。
在CustomerDao.xml中,編寫執行插入操做的映射插入語句,代碼以下所示。
<!-- 根據id查詢用戶 --> <select id="queryCustomerById" parameterType="Integer" resultType="com.itheima.core.pojo.Customer"> SELECT * FROM customer WHERE cust_id = #{id} </select> <!-- 更新客戶 --> <update id="updateCustomerById" parameterType="com.itheima.core.pojo.Customer"> UPDATE customer <set> <if test="cust_name !=null and cust_name != ''"> cust_name = #{cust_name}, </if> <if test="cust_user_id !=null"> cust_user_id = #{cust_user_id}, </if> <if test="cust_create_id !=null"> cust_create_id = #{cust_create_id}, </if> <if test="cust_source !=null and cust_source != ''"> cust_source = #{cust_source}, </if> <if test="cust_industry !=null and cust_industry != ''"> cust_industry = #{cust_industry}, </if> <if test="cust_level !=null and cust_level != ''"> cust_level = #{cust_level}, </if> <if test="cust_linkman !=null and cust_linkman != ''"> cust_linkman = #{cust_linkman}, </if> <if test="cust_phone !=null and cust_phone != ''"> cust_phone = #{cust_phone}, </if> <if test="cust_mobile !=null and cust_mobile != ''"> cust_mobile = #{cust_mobile}, </if> <if test="cust_zipcode !=null and cust_zipcode != ''"> cust_zipcode = #{cust_zipcode}, </if> <if test="cust_address !=null and cust_address != ''"> cust_address = #{cust_address}, </if> <if test="cust_createtime !=null andcust_createtime != ''"> cust_createtime = #{cust_createtime}, </if> </set> WHERE cust_id = #{cust_id} </update>
至此,修改客戶的實現代碼就已經編寫完成。發佈並啓動項目後,進入客戶管理頁面,單擊列表中編號爲14客戶後面的「修改」按鈕,將所屬行業修改成「對外貿易」,並將客戶級別修改成「VIP客戶」,如圖18-20所示。
單擊圖18-20中的「保存修改」按鈕後,若是程序正確執行,將會出現「客戶信息更新成功」提示框,確認後,將回到客戶管理列表頁面,此時頁面中的信息如圖18-21所示。
從圖18-21能夠看出,編號爲14客戶的修改後信息已經顯示在列表中,這也就說明系統的修改客戶功能已成功實現。
刪除客戶是客戶管理模塊中的最後一個功能。在單擊客戶信息列表中操做列的某個「刪除」連接後,會彈出刪除確認框,如圖18-22所示。
單擊「肯定」按鈕後,便可執行刪除客戶的操做。接下來,本節將對刪除客戶功能的實現進行詳細講解,具體步驟以下。
頁面中,刪除客戶連接的實現代碼以下所示。
<a href="#" class="btn btn-danger btn-xs" onclick="deleteCustomer(${row.cust_id})">刪除</a>
上述代碼中,當單擊「刪除」後,會執行onclick屬性中的deleteCustomer()方法,該方法中的參數${row.cust_id}會獲取當前所在行的客戶id。在頁面中,編寫刪除客戶的方法deleteCustomer(),其方法代碼以下所示。
// 刪除客戶 function deleteCustomer(id) { if(confirm('確實要刪除該客戶嗎?')) { $.post("<%=basePath%>customer/delete.action",{"id":id}, function(data){ if(data =="OK"){ alert("客戶刪除成功!"); window.location.reload(); }else{ alert("刪除客戶失敗!"); window.location.reload(); } }); } }
執行上述方法時,會經過jQuery Ajax的方式發送一個以「/delete.action」結尾的請求,該請求會將所要刪除的客戶id傳入後臺處理方法中。
在CustomerController類中,建立一個刪除客戶的方法customerDelete(),編輯後的實現代碼以下所示。
/** * 刪除客戶 * * @param id * @return */ @RequestMapping("/customer/delete") @ResponseBody public String deleteCustomerById(Long id) { int rows = this.customerService.deleteCustomerById(id); if(rows >0) { return "ok"; }else { return "FAIL"; } }
customerDelete()方法並無執行太多操做,而是調用了Service層中的deleteCustomer()方法來獲取數據庫中受影響的行數,若是其值大於0,則表示刪除成功,不然表示刪除失敗。
(1)建立接口方法。在CustomerService中,編寫一個刪除客戶的方法,代碼以下所示。
int deleteCustomerById(Long id);
(2)建立實現類方法。在CustomerServiceImpl中,實現接口中的刪除方法,編輯後的代碼以下所示。[插圖]
@Override public int deleteCustomerById(Long id) { return customerDao.deleteCustomerById(id); }
(1)建立接口方法。在CustomerDao接口中,編寫經過id刪除客戶的方法,代碼以下所示。
int deleteCustomerById(Long id);
(2)建立映射語句。在CustomerDao.xml中編寫執行刪除操做的映射語句,代碼以下所示。
<!-- 根據id刪除客戶 --> <delete id="deleteCustomerById" parameterType="Long"> DELETE FROM customer WHERE cust_id = #{id} </delete>
至此,刪除客戶功能的實現代碼就已經編寫完成。下面以刪除編號爲14的客戶「小張」爲例,來測試系統的刪除功能。
單擊編號爲14客戶所在行的「刪除」連接後,會彈出刪除確認框,單擊「肯定」按鈕後,頁面中會彈出客戶刪除成功的提示框,如圖18-23所示。肯定後,系統會刷新當前頁面。此時頁面中客戶信息列表所顯示的數據如圖18-24所示。從圖18-24能夠看到,執行刪除操做後,編號爲14的客戶「小張」並無在客戶信息列表中顯示,這也就說明刪除操做執行成功。