py-18-PROJECT4

目录:

day01:京淘目-权限管理子系统-系统概述

day02:京淘项目-权限管理子系统-配置管理

day03:京淘项目-权限管理子系统-菜单模块

day04:京淘项目-权限管理子系统-角色模块

day05:京淘项目-权限管理子系统-用户模块

day06:京淘项目-权限管理子系统-Shiro安全框架

day07:京淘项目-权限管理子系统-Spring AOP

day08:京淘项目-权限管理子系统-Spring事务管理


day01:京淘项目-权限管理子系统-系统概述

1.    系统需求概要                                       1

1.1.    系统功能模块需求                             1

1.2.    系统非功能需求                                2

2.    系统架构设计                                        2

2.3.    整体分层架构                                    2

2.4.    MVC架构实现                                   4

3.    系统数据库设计                                    4

3.5.    配置表设计                                        5

3.6.    菜单表设计                                        5

3.7.    角色表设计                                        5

3.8.    组织表设计                                        5

3.9.    用户表设计                                        5

3.10.    角色菜单关系表设计                       6

3.11.    角色用户关系设计                           6

3.12.    日志表设计                                      6

4.    系统环境初始化                                    6

4.13.    创建Maven项目                               6

4.14.    项目技术整合                                  8

4.15.    项目首页设计                                  9

5.    总结                                                    10

5.16.    重点和难点分析                             10

5.17.    常见FAQ                                        10


  1. 系统需求概要

    1. 系统功能模块需求

本权限管理子系统分为组织管理(部门管理),菜单管理(资源管理),角色管理,用户管理,日志管理,配置管理,系统权限管理(认证,授权),退出等.


2.系统非功能需求

非功能性需求是指依一些条件判断系统运作情形或其特性,而不是针对系统特定行为的需求。包括安全性、可靠性、互操作性、健壮性、易使用性、可维护性、可移植性、可重用性、可扩充性。

例如:


 2.系统架构设计

3.整体分层架构

对于整个系统采用分层架构设计,目的是更好实现系统的可维护性以及可扩展性.

本项目中的设计采用分层架构思想实现.

  1. 开放接口层:可直接封装 Service 方法暴露成 RPC 接口;通过 Web 封装成 http 接口;进行 网关安全控制、流量控制等。

  2. 终端显示层:各个端的模板渲染并执行显示的层。当前主要是JS 渲染, JSP 渲染,移动端展示等。

  3. Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。

  4. Service 层:相对具体的业务逻辑服务层。

  5. Manager 层:通用业务处理层,它有如下特征: 

    1. 对第三方平台封装的层,预处理返回结果及转化异常信息; 

    2. 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;  

    3. 与 DAO 层交互,对多个 DAO 的组合复用。

  6. DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。

  7. 外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口


 4.MVC架构实现

项目代码基本架构

客户端技术:

  1. HTML,CSS,JS

  2. Bootstrap,AdminLTE,JQuery,Ajax

服务端技术:

  1. CoreJava,Jdbc,Servlet

  2. Spring,SpringMVC,MyBatis,Shiro,DRUID,FastJson,Jackson,Log4j,...

数据库:

  1. MySql

  2. ......

常用网址:

https://github.com/

Free Bootstrap Admin Template | AdminLTE.IO


3.系统数据库设计

数据库名:jtsys

数据库编码:utf-8

数据库表应用引擎:InnoDB

文件寻贴主得


 5.配置表设计

表名: sys_configs

引擎: InnoDB

字段:


6.菜单表设计

表名:sys_menus

引擎:InnoDB

字段:


7.角色表设计

表名: sys_roles

引擎: InnoDB

字段:


8.组织表设计

表名:sys_orgs

引擎:InnoDB

字段:


9.用户表设计

表名:sys_users

引擎:InnoDB

字段:


10.角色菜单关系表设计

表名:sys_role_menus

引擎:InnoDB

字段:


11.角色用户关系设计

表名:sys_user_roles

引擎:InnoDB

字段:


12.日志表设计

表名:sys_logs

引擎:InnoDB

字段:


4.系统环境初始化

13.创建Maven项目

  1. 创建Maven WEB项目(CGB-JT-SYS-V1.01)

step01:

step02:

2.配置Maven WEB 项目

  1. 添加两个maven插件(war,compile)

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<failOnMissingWebXml>flase</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

项目报错需要强制更新Maven项目

3.设置目标运行环境(targeted runtimes)

4.设置编译版本(project facets)

5.设置项目编码(utf-8)

思考:

  1. 为什么要设置目标运行环境

  2. ...


14.项目技术整合

  1. 添加项目依赖

Spring-webmvc,jackson,mysql,druid,mybatis,mybatis-spring,

spring-jdbc,log4j,junit

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.23</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 整合jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>

2.添加项目配置文件configs.properties,log4j.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///cgbmybatis
uname:root
password:root
log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%-5p] %c - %m%n
log4j.logger.com.mybatis3=DEBUG
log4j.logger.com.jt=DEBUG

3.创建(3个配置类WebAppInitializer,AppServletConfig,AppRootConfig)

WebAppInitializer.java

public class JtWebAppInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer{
/**
* 此方法负责加载service,dao等对象
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AppRootConfig.class};
}
/***
* 此方法负责加载Controllers,ViewResolver,HandlerMapper
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return  new Class<?>[] { AppServletConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] {"*.do"};
}

}

AppServletConfig.java

/**扫描com.jt包及子包中使用
*Controller注解修饰的类,并进行加载
*1)includeFilters 用于设置相关加载条件
*/
@Configuration
@ComponentScan("com.jt")
@EnableWebMvc
public class AppServletConfig extends WebMvcConfigurerAdapter {


/**
* 视图解析器
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
super.configureViewResolvers(registry);
registry.jsp("/WEB-INF/pages/", ".html");
}


}

AppRootConfig.java

@Configuration
@PropertySource(value="classpath:config.properties")
public class AppRootConfig {

/**配置DRUID连接池*/
@Lazy(false)
@Bean(value="dataSource",initMethod="init",destroyMethod="close")
public DruidDataSource newDruidDataSource(
@Value("${driver}")  String driver,
@Value("${url}")     String url,
@Value("${uname}")   String username,
@Value("${password}")      String  password) {

DruidDataSource dds = new DruidDataSource();
dds.setDriverClassName(driver);
dds.setUrl(url);
dds.setUsername(username);
dds.setPassword(password);
return dds;
}

/**
* 配置sqlSessionFactory
*/
@Bean("sqlSessionFactory")
public SqlSessionFactoryBean newSqlSessionFactoryBean(
@Autowired DataSource dataSource) throws IOException{
SqlSessionFactoryBean fBean=new SqlSessionFactoryBean();
fBean.setDataSource(dataSource);
Resource[] mapperLocations=
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/sys/*.xml");
fBean.setMapperLocations(mapperLocations);
return fBean;
}


}

4.拷贝AdminLTE 静态资源文件(CSS,JS,Images)到项目的webapp目录下

该文件寻贴主得

项目报错是因为Eclipse无法识别分散的js文件

4.拷贝pages 页面文件到项目的WEB-INF目录下

4.部署项目启动运行tomcat,检测启动时是否有问题,假如没有问题,整合OK


15.项目首页设计 

业务描述: 创建首页Controller对象,通过此对象的方法呈现首页页面

  1. 创建Controller类

包名:com.jt.sys.controller

类名:PageController

映射:@RequestMapping("/")

2.Controller类中添加方法返回首页页面.(starter.html)

方法名:indexUI

参数列表:()

返回值:String

映射:@RequestMapping("indexUI")

代码实现:

@RequestMapping("/")

@Controller

public class PageController {

@RequestMapping("doIndexUI")

public String doIndexUI(){

return "starter";

}

}

部署项目:访问

http://localhost:8080/CGB-JT-SYS-V1.01/doIndexUI.do

效果:

3.点击系统管理显示出下拉选项

4.点击配置管理显示右侧信息

代码实现:

5、在controller类中处理请求,相应页面:

6、实现以下效果:


day02:京淘-项目权限管理子系统-配置管理

1.    配置管理列表页面呈现                                             2

1.1.    服务端实现                                                            2

1.1.1.    Controller实现                                                   2

1.2.    客户端实现                                                          3

1.2.2.    首页页面starter.html配置管理事件处理            3

1.2.3.    创建配置列表页面config_list.html                    4

2.    配置管理列表数据呈现                                          4

2.3.    服务端实现                                                         4

2.3.4.    Entity类实现                                                    4

2.3.5.    Dao接口实现                                                  6

2.3.6.    Mapper文件实现                                             7

2.3.7.    Service接口及实现类                                       9

2.3.8.    Controller类的实现                                          12

2.4.    客户端实现                                                           15

2.4.9.    列表页面config_list.html配置信息呈现                15

2.4.10.    列表页面config_list.html分页信息呈现               17

2.4.11.    列表页面config_list.html信息查询实现               19

3.    配置管理删除操作实现                                            20

3.5.    服务端的实现                                            20

3.5.12.    Dao接口实现                                      20

3.5.13.    Mapper文件实现                                   21

3.5.14.    Service接口实现                                  21

3.5.15.    Controller实现                                   22

3.6.    客户端的实现                                            23

3.6.16.    配置列表页面实现                                  23

4.    配置管理添加页面呈现                                            25

4.7.    服务端实现                                              25

4.7.17.    Controller实现                                   25

4.8.    客户端实现                                              26

4.8.18.    配置列表页面实现                                  26

5.    配置管理数据添加实现                                            27

5.9.    服务端实现                                              27

5.9.19.    Dao实现                                          27

5.9.20.    Mapper实现                                       28

5.9.21.    Service实现                                      28

5.9.22.    Controller实现                                   29

5.10.    客户端实现                                             30

5.10.23.    配置管理编辑页面实现                              30

6.    配置管理编辑页面数据呈现                                        31

6.11.    服务端实现                                             31

6.11.24.    Controller 实现                                 31

6.12.    客户端实现                                             32

6.12.25.    配置列表页面                                     32

6.12.26.    配置编辑页面                                     33

7.    配置管理数据更新操作实现                                        34

7.13.    服务端实现                                             34

7.13.27.    Dao实现                                         34

7.13.28.    Mapper实现                                      35

7.13.29.    Service实现                                     35

7.13.30.    Controller实现                                  36

7.14.    客户端实现                                             37

7.14.31.    编辑页面事件处理                                 37

8.    总结                                                          38

8.15.    重点和难点分析                                          38

8.16.    常见FAQ                                               38


  1. 配置管理列表页面呈现

    1. 服务端实现

      1. Controller实现

业务描述

  1. 定义配置管理Controller处理配置管理的客户端请求

  2. 修改首页Controller添加pageUI方法返回page页面

业务实现

1.配置管理Controller类的定义

a.包名 com.jt.sys.controller

b.类名SysConfigController

c.映射 “/config/”

2.配置管理Controller中方法定义(添加返回页面的相关方法)

a.方法名 doConfigListUI

b.参数列表(无)

c.返回值类型:String

d.Url映射:doConfigListUI

代码实现:

@Controller
@RequestMapping("/config/")
public class SysConfigController {
@RequestMapping("doConfigListUI")
public String doConfigListUI(){
return "sys/config_list";
}
}

2.客户端实现

2.首页页面starter.html配置管理事件处理

页面描述

  1. 准备配置列表页面(WEB-INF/pages/sys/config_list.html)

  2. 点击配置管理菜单时异步加载配置列表页面。

业务实现

  1. 事件注册(被点击的元素上)

  2. 事件处理函数定义

代码实现:

$(function(){
$("#load-config-id").click(function(){
$("#mainContentId").load("config/doConfigListUI.do");
});
})

load函数为jquery中的ajax异步请求函数。


3.创建配置列表页面config_list.html

业务描述

1) 在WEB-INF/pages/sys目录下定义config_list.html页面.

2) 当页面加载完成以后异步加载分页页面(page.html)。

业务实现

  1. 在PageController中添加doPageUI方法返回page页面

  2. 在config_list.html页面中异步加载page页面页面

关键代码实现:config_list.html

$(function(){
$("#pageId").load("doPageUI.do");
});

2.配置管理列表数据呈现

3.服务端实现

4.Entity类实现

业务描述:

定义实体封装从数据库查询的数据

业务实现

构建与sys_configs表对应的实体类型

a.包名 com.jt.sys.entity

b.类名 SysConfig (实现序列化接口,并定义序列化id)

c.属性 与表(sys_configs)中字段有对应关系

d.方法 set/get/toString

关键代码实现:

public class SysConfig implements Serializable{
private static final long serialVersionUID = 7179456199425880466L;
private Integer id;
private String name;
private String value;
private String note;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
@Override
public String toString() {
return "SysConfig [id=" + id + ", name=" + name + ", value=" + value + ", note=" + note + ", createdTime="
+ createdTime + ", modifiedTime=" + modifiedTime + ", createdUser=" + createdUser + ", modifiedUser="
+ modifiedUser + "]";
}
}

说明:

通过此对象除了可以封装从数据库查询的数据,还可以封装客户端请求数据,实现层与层之间数据的传递。


5.Dao接口实现

业务描述:(核心-查询当前页显示的数据以及总记录数)

  1. 接收业务层参数数据

  2. 基于参数进行数据查询

  3. 将查询结果进行封装

  4. 将结果返回给业务层对象

代码实现:创建接口并定义相关方法。

1.Dao接口定义

a.包名: com.jt.sys.dao

b.名字: SysConfigDao

2.方法定义:负责基于条件查询当前页数据

a.方法名:findPageObjects

b.参数列表:(String name,Integer startIndex,Integer pageSize)

c.返回值:List<SysConfig>

3.方法定义:负责基于条件查询总记录数

a.方法名:getRowCount

b.参数列表:(String name)

c.返回值:int

代码实现:

public interface SysConfigDao {
List<SysConfig> findPageObjects(
@Param("name")String name,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize);
int getRowCount(@Param("name")String name);
}

6.Mapper文件实现

业务描述

  1. 基于Dao接口创建映射文件

  2. 基于Dao方法在映射文件中创建映射元素建映射元素

业务实现:

1.创建映射文件

a.包名:mapper.sys

b.文件名:SysConfigMapper.xml

c.命名空间 com.jt.sys.dao.SysConfigDao

2.创建映射元素实现翻页查询操作

a.元素名  select

b.元素id  findPageObjects

c.参数类型 (不写)

d.结果类型 com.jt.sys.entity.SysConfig

e.SQL定义 select * from sys_configs where name like ? limit ?,?

3.创建映射元素实现查询统计操作

a.元素名  select

b.元素id  getRowCount

c.参数类型 (不写)

d.结果类型 int

e.SQL定义 select count(*) from sys_configs where name like ?

代码实现:

<?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.jt.sys.dao.SysConfigDao">
<select id="findPageObjects"
resultType="com.jt.sys.entity.SysConfig">
select *
from sys_configs
<include refid="queryWhereId"/>
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount"
resultType="int">
select count(*)
from sys_configs
<include refid="queryWhereId"/>
</select>
<!-- 借助此元素对共性数据进行提取 -->
<sql id="queryWhereId">
<where>
<if test="name!=null and name!=''">
name like concat("%",#{name},"%")
</if>
</where>
</sql>
</mapper>

7.Service接口及实现类

业务描述:核心业务就是分页查询数据并对数据进行封装。

  1. 通过参数变量接收控制层数据

  2. 对数据进行合法验证

  3. 基于参数数据进行总记录数查询

  4. 基于参数数据进行当前页记录的查询

  5. 对数据进行封装

  6. ..........

业务实现:

  1. 业务值对象定义:(封装分页信息以及当前数据)

a.包名 com.jt.common.vo (封装值的对象)

b.类名 PageObject<T> (实现序列化接口并添加序列化id)

c.属性 (总行数,总页数,当前页码,页面大小,当前页记录信息)

d.方法 (set/get)

代码实现:

public class PageObject<T> implements Serializable {
private static final long serialVersionUID = 6780580291247550747L;//类泛型
/**当前页的页码值*/
private Integer pageCurrent=1;
/**页面大小*/
private Integer pageSize=3;
/**总行数(通过查询获得)*/
private Integer rowCount=0;
/**总页数(通过计算获得)*/
private Integer pageCount=0;
/**当前页记录*/
private List<T> records;
public Integer getPageCurrent() {
return pageCurrent;
}
public void setPageCurrent(Integer pageCurrent) {
this.pageCurrent = pageCurrent;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getRowCount() {
return rowCount;
}
public void setRowCount(Integer rowCount) {
this.rowCount = rowCount;
}
public Integer getPageCount() {
//计算总页数,为什么会在此方法进行计算
//将对象转换为json串时底层调用的是对象的get方法
pageCount=rowCount/pageSize;
if(rowCount%pageSize!=0){
pageCount++;
}
return pageCount;
}
public void setPageCount(Integer pageCount) {
this.pageCount = pageCount;
}
public List<T> getRecords() {
return records;
}
public void setRecords(List<T> records) {
this.records = records;
}
}

2.接口定义:为控制层请求提供服务

a.包名 com.jt.sys.service

b.类名 SysConfigService

3.接口方法定义:

a.方法名 findPageObjects

b.参数列表 (String name,Integer pageCurrent)

c.返回值 PageObject<SysConfig>

关键代码实现:

public interface SysConfigService {
/**
* 通过此方法实现分页查询操作
* @param name 基于条件查询时的参数名
* @param pageCurrent 当前的页码值
* @return 当前页记录+分页信息
*/
PageObject<SysConfig> findPageObjects(
String name,
Integer pageCurrent);
}

4.接口实现类的定义:

a.包名 com.jt.sys.service.impl

b.类名 SysConfigServiceImpl

代码实现

@Service
public class SysConfigServiceImpl implements SysConfigService{
@Autowired
private SysConfigDao sysConfigDao;
@Override
public PageObject<SysConfig> findPageObjects(
String name, Integer pageCurrent) {
//1.验证参数合法性
//1.1验证pageCurrent的合法性,
//不合法抛出IllegalArgumentException异常
if(pageCurrent==null||pageCurrent<1)
throw new IllegalArgumentException("当前页码不正确");
//2.基于条件查询总记录数
//2.1) 执行查询
int rowCount=sysConfigDao.getRowCount(name);
//2.2) 验证查询结果,假如结果为0不再执行如下操作
if(rowCount==0)return null;
//3.基于条件查询当前页记录(pageSize定义为2)
//3.1)定义pageSize
int pageSize=2; 
//3.2)计算startIndex
int startIndex=(pageCurrent-1)*pageSize;
//3.3)执行当前数据的查询操作
List<SysConfig> records=
sysConfigDao.findPageObjects(name, startIndex, pageSize);
//4.对分页信息以及当前页记录进行封装
//4.1)构建PageObject对象
PageObject<SysConfig> pageObject=
new PageObject<>();
//4.2)封装数据
pageObject.setPageCurrent(pageCurrent);
pageObject.setPageSize(pageSize);
pageObject.setRowCount(rowCount);
pageObject.setRecords(records);
//5.返回封装结果。
return pageObject;
}
}

8.Controller类的实现

业务描述:核心业务是处理客户端请求

  1. 接收客户端请求中的数据

  2. 基于请求调用业务方法进行请求处理

  3. 对处理结果进行封装(JsonResult)

  4. 将结果转换为json格式的字符串

  5. 将字符串通过服务器输出到客户端。

业务实现:

1.值对象定义:(封装控制层方法的返回结果)

a.包名: com.jt.common.vo

b.类名: JsonResult

c.属性: (状态码-int state=1,状态信息-String message="ok",正确数据-Object data)

d.方法: (set/get,构造方法)

2.Controller方法定义:(在SysConfigController中定义请求处理方法)

a.方法名  doFindPageObjects

b.参数列表 (String  name,Integer pageCurrent)

c.返回值  JsonResult

d.映射  doFindPageObjects

3.统一异常类的定义

a.包名 com.jt.common.controller

b.类名 ControllerExceptionHandler

c.注解 @ControllerAdvice

4.异常类中方法定义(处理参数异常的方法)

a.方法名 doHandleException

b.参数列表 RuntimeException

c.返回值 JsonResult

d.注解描述(@ResponseBody,@ExceptionHandler)

关键代码实现:

封装控制层值的对象

public class JsonResult implements Serializable {
private static final long serialVersionUID = -856924038217431339L;//SysResult/Result/R
/**状态码*/
private int state=1;//1表示SUCCESS,0表示ERROR
/**状态信息*/
private String message="ok";
/**正确数据*/
private Object data;
public JsonResult() {
}
public JsonResult(String message){
this.message=message;
}
/**一般查询时调用,封装查询结果*/
public JsonResult(Object data) {
this.data=data;
}
/**出现异常时时调用*/
public JsonResult(Throwable t){
this.state=0;
this.message=t.getMessage();
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}

Controller方法定义

@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects(String name,Integer pageCurrent){
PageObject<SysConfig> pageObject=
sysConfigService.findPageObjects(name,pageCurrent);
return new JsonResult(pageObject);
}

统一异常类及方法定义

@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public JsonResult doHandleException(RuntimeException e){
e.printStackTrace();
return new JsonResult(e);
}
}

运行进行测试:

http://localhost:8080/CGB-JT-SYS-V1.01/config/doFindPageObjects.do?name=Path&pageCurrent=1

出现以下数据表示成功


4.客户端实现

9.列表页面config_list.html配置信息呈现

业务描述:

  1. 页面加载完成发起异步请求加载配置信息

  2. 通过服务端返回的数据更新当前列表页面

业务实现

  1. 定义doGetObjects()函数,通过此函数执行异步加载操作。

  2. 分页页面加载完成以后调用doGetObjects().

关键代码实现:

$(function(){
//为什么要将doGetObjects函数写到load函数对应的回调内部。
$("#pageId").load("pageUI.do",function(){
doGetObjects();
});
}

分页查询函数定义

function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url="config/doFindPageObjects.do"
//? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)
//此数据会在何时进行绑定?(setPagination,doQueryObjects)
var pageCurrent=$("#pageId").data("pageCurrent");
//为什么要执行如下语句的判定,然后初始化pageCurrent的值为1
//pageCurrent参数在没有赋值的情况下,默认初始值应该为1.
if(!pageCurrent) pageCurrent=1;
var params={"pageCurrent":pageCurrent};
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//JsonResult->PageObject->List<SysConfigs>+...
//请问result是一个字符串还是json格式的js对象?对象
doSetResponseResult(result);
}
);
}

设置异步响应结果

function doSetResponseResult(result){
if(result.state==1){//ok
//服务端返回的数据为空时,初始化页面内容
if(!result.data){
doInitElementContent();
return;
}
//更新table中tbody内部的数据
setTableBodyRows(result.data.records);//将数据呈现在页面上
//更新页面page.html分页数据
setPagination(result.data);
}else{
alert(result.msg);
} 
}

假如没有记录则初始化当前页面为空,并给出提示

function doInitElementContent(){
//初始化表格tbody中数据
doInitTableBody();
//初始化表格分页信息
doInitPagination();
}
function doInitTableBody(){
var tBody=$("#tbodyId");
tBody.empty();
var tr="<tr><td align='center' colspan=6>没有找到对应记录</td></tr>"
tBody.append(tr);
}

将异步响应结果呈现在table的tbody位置

/*设置表格内容*/
function setTableBodyRows(records){
//1.获取tbody对象,并清空对象
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代records记录,并将其内容追加到tbody
for(var i in records){
//2.1 构建tr对象
var tr=$("<tr></tr>");
//2.2 构建tds对象
var tds=createdTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}

创建当前行的td元素

function createdTds(row){
var tds="<td><input type='checkbox' class='cBox' name='cItem'
value='"+row.id+"'></td>"+
"<td>"+row.name+"</td>"+
"<td>"+row.value+"</td>"+
"<td>"+new Date(row.createdTime).toLocaleDateString()+"</td>"+
"<td>"+new Date(row.modifiedTime).toLocaleDateString()+"</td>"+
"<td><button type='button' class='btn btn-default
btn-update'>update</button></td>";
return tds;
}

效果:


10.列表页面config_list.html分页信息呈现

业务描述

  1. 配置信息异步加载完成以后初始化分页数据(调用setPagination函数)

  2. 点击分页元素时异步加载当前页(pageCurrent)数据(调用jumpToPage)

业务实现 (page.html页面中定义JS函数)

  1. 定义setPagination方法(实现分页数据初始化)

  2. 定义jumpToPage方法(通过此方法实现当前数据查询)

  3. Page.html页面加载完成以后在对应元素上注册click事件

关键代码实现:

$(function(){
//事件注册
$("#pageId").on("click",".first,.pre,.next,.last",jumpToPage);
});
function jumpToPage(){

//1.获取点击对象的class值
var cla = $(this).prop("class");
//2.基于点击的对象执行pageCurrent值的修改
//2.1获取pageCurrent(当前页码),pageCount(总页数)的当前值
var pageCurrent = $("#pageId").data("pageCurrent");
var pageCount = $("#pageId").data("pageCount");
//2.2修改pageCurrent的值
if(cla=="first"){
pageCurrent = 1;
}else if(cla == "pre"&& pageCurrent>1){
pageCurrent--;
}else if(cla == "next" && pageCurrent<pageCount ){
pageCurrent++;
}else if(cla =="last"){
pageCurrent=pageCount;
}
//3.对pageCurrent值进行重新绑定
$("#pageId").data("pageCurrent",pageCurrent);
//4.基于新的pageCurrent的值进行当前页数据查询
doGetObjects();

}
/**
*页面数据按钮上的文字绑定
*/
function  doInitPagination(){
$(".rowCount").html("总记录数(0)");
$(".pageCount").html("总页数(0)");
$(".pageCurrent").html("当前页(1)");
}
/**
*更新页面page.html分页数据
*/
function setPagination(page){
//1.初始化数据
$(".rowCount").html("总记录数("+page.rowCount+")");
$(".pageCount").html("总页数("+page.pageCount+")");
$(".pageCurrent").html("当前页("+page.pageCurrent+")");
//2.绑定数据(为后续对此数据的使用提供服务)
$("#pageId").data("pageCount",page.pageCount);
$("#pageId").data("pageCurrent",page.pageCurrent);
}

效果:


11.列表页面config_list.html信息查询实现

业务说明

  1. 列表查询按钮事件注册

  2. 列表查询按钮事件处理函数定义

  3. 列表查询参数获取以及传递

业务实现:

  1. 在$(function(){})回调函数中追加查询按钮的事件注册。

  2. 定义查询按钮的事件处理函数doQueryObjects

  3. 重用doGetObjects函数并添加查询参数name

关键代码实现:

查询按钮事件注册

/**
*查询按钮事件注册
*/
$(function (){
$(".input-group-btn").on("click",".btn-search",doQueryObjects);
});

查询按钮事件处理函数定义

/**
*查询按钮事件处理函数定义
*/
function doQueryObjects(){
//为什么要在此位置初始化pageCurrent的值为1?
//数据查询时页码的初始位置也应该是第一页
$("#pageId").data("pageCurrent",1);
//为什么要调用doGetObjects函数?
//重用js代码,简化jS代码编写。
doGetObjects();
}

在分页查询函数中追加name参数定义

/**
*分页查询函数定义
*/
function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url = "config/doFindPageObjects.do";
//? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)
//此数据会在何时进行绑定?(setPagination,doQueryObjects)
var pageCurrent = $("#pageId").data("pageCurrent");
//为什么要执行如下语句的判定,然后初始化pageCurrent的值为1
//pageCurrent参数在没有赋值的情况下,默认初始值应该为1.
if(!pageCurrent) pageCurrent=1;
var params={"pageCurrent":pageCurrent};

//为什么此位置要获取查询参数的值?
//一种冗余的应用方法,目的时让此函数在查询时可以重用。
var name = $("#searchNameId").val();
//将输入框输入的数据绑定到数据(params)上
if(name) params.name=name;
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//JsonResult->PageObject->List<SysConfigs>+...
//请问result是一个字符串还是json格式的js对象?对象
doSetResponseResult(result);
});

}

效果图:


3.配置管理删除操作实现

5.服务端的实现

12.Dao接口实现

业务描述

  1. 接收业务层参数数据(String[] ids)

  2. 基于数据执行删除操作

业务实现

在SysConfigDao接口中定义删除方法

  1. 方法名 deleteObjects

  2. 方法参数 (String[] ids)

  3. 方法返回值 int

关键代码实现:

int deleteObjects(@Param("ids")Integer... ids);

13.Mapper文件实现

业务描述

基于SysConfigDao中方法的定义,编写删除元素。

业务实现:

在SysConfigMapper.xml文件中定义删除元素

  1. 元素名  delete

  2. 元素id deleteObjects

  3. Sql定义 : delete from sys_configs where id in (?,?,?)

关键代码实现:

<delete id="deleteObjects">
delete from sys_configs
where id in
<foreach collection="ids"
open="("
close=")"
separator=","
item="item">
#{item}
</foreach>
</delete>

14.Service接口实现

业务描述

  1. 接收控制层数据并进行合法验证

  2. 基于业务层数据执行删除操作

  3. 对删除过程进行监控(先进行异常捕获)

  4. 对删除结果进行验证并返回

业务实现

在SysConfigService接口及实现类中添加方法

  1. 方法名 DeleteObjects

  2. 参数列表 Integer... ids

  3. 返回值 int

关键代码实现:

@Override
public int deleteObjects(Integer... ids) {
//1.验证合法性
if(ids ==null || ids.length ==0) {
throw new IllegalArgumentException("必须要选中删除的内容");
}
//3.执行删除操作
int rows;
try {
rows = sysConfigDao.deleteObjects(ids);
} catch (Throwable e) {
e.printStackTrace();
//发出报警信息(例如给运维人员发短信)
throw new ServiceException("系统故障,正在恢复中...");
}

//4.对结果进行验证
if(rows ==0) {
throw new ServiceException("记录可能已经不存在");
}

return rows;
}

15.Controller实现

业务描述

  1. 接收客户端请求数据

  2. 调用业务层方法执行删除操作

  3. 封装结果并返回

业务实现

在SysConfigController中定义删除方法

  1. 方法名 doDeleteObjects

  2. 参数列表 String ids

  3. 返回值 JsonResult

  4. 映射 doDeleteObjects

关键代码实现:

@RequestMapping("doDeleteObjects")
@ResponseBody
public JsonResult doDeleteObjects(Integer... ids){
sysConfigService.deleteObjects(ids);
return new JsonResult("delete ok");
}

6.客户端的实现

16.配置列表页面实现

业务描述:

  1. 页面全选操作实现

  2. 页面删除按钮事件注册

  3. 页面删除操作事件处理函数定义

业务实现:

  1. Thead中全选checkbox元素事件注册及事件处理函数doCheckAll定义

  2. Tbody中checkbox元素事件注册及事件处理函数doChangeCheckAllState定义

  3. 在$(function(){})回调函数中追加删除按钮的事件注册操作。

  4. 定义事件处理函数doDeleteObjects,处理删除按钮的点击操作。

关键代码实现

全选按钮注册

/**
*全选按钮注册
*/
$(function (){
$("thead").on("change","#checkAll",doCheackAll);
});
/**
*tbody中checkbox对象事件注册
*/
$(function (){
$("#tbodyId").on("change",".cBox",doChangeCheckAllState);
});

Tbody中checkbox的状态影响thead中全选元素的状态

//当tbody中checkbox的状态发生变化以后
//修改thead中全选元素的状态值。
function doChangeCheckAllState(){
//1.设定默认状态值
var flag=true;
//2.迭代所有tbody中的checkbox值并进行与操作
$("#tbodyId input[name='cItem']")
.each(function(){
flag=flag&$(this).prop("checked")
});
//3.修改全选元素checkbox的值为flag
$("#checkAll").prop("checked",flag);
}

Thead中全选元素的状态影响tbody中checkbox对象状态

/*实现全选操作*/
function doCheckAll(){
//1.获取当前点击对象的checked属性的值
var flag=$(this).prop("checked");//true or false
//2.将tbody中所有checkbox元素的值都修改为flag对应的值。each:元素迭代
//第一种方案
/* $("#tbodyId input[name='cItem']")
.each(function(){
$(this).prop("checked",flag);
}); */
//第二种方案
$("#tbodyId input[name='cItem']")
.prop("checked",flag);
}

删除按钮事件处理函数定义

/**
*查询、删除、按钮事件注册
*/
$(function (){
$(".input-group-btn")
.on("click",".btn-search",doQueryObjects)
.on("click",".btn-delete",doDeleteObjects);
});
/*执行删除操作*/
function doDeleteObjects(){
//1.获取选中的id值
var ids=doGetCheckedIds();
if(ids.length==0){
alert("至少选择一个");
return;
}
//2.发异步请求执行删除操作
var url="config/doDeleteObjects.do";
var params={"ids":ids.toString()};
console.log(params);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doGetObjects();
}else{
alert(result.message);
}
});
}

获取用户选中的记录id并存储到数组

function doGetCheckedIds(){
//定义一个数组,用于存储选中的checkbox的id值
var array=[];//new Array();
//获取tbody中所有名字为cItem的input元素
$("#tbodyId input[name=cItem]").
//迭代这些元素,每发现一个元素都会执行如下回调函数
each(function(){
//假如此元素的checked属性的值为true
if($(this).prop("checked")){
//调用数组对象的push方法将选中对象的值存储到数组
array.push($(this).val());
}
});
return array;
}

4.配置管理添加页面呈现

7.服务端实现

17.Controller实现

业务描述

基于客户端请求返回一个页面

业务实现:

在SysConfigController中添加返回编辑页面的方法

  1. 方法名 editUI

  2. 参数列表 ()

  3. 返回值 String

  4. 方法映射 editUI

关键代码实现

/**
* 添加页面
*/
@RequestMapping("doConfigEditUI")
public String doConfigEditUI() {
return "sys/config_edit";
}

8.客户端实现

18.配置列表页面实现

业务描述

  1. 添加按钮事件注册

  2. 添加按钮事件处理函数定义

业务实现:

  1. 在$(function(){})事件处理函数内部追加事件注册

  2. 定义表页面中添加按钮的事件处理函数

关键代码实现

列表页面中添加按钮事件注册

/WEB-INF/pages/sys/config_list.html

$(function(){
…
$(".input-group-btn").on("click",".btn-add",doShowEditDialog);
}

点击添加按钮时调用此函数在模态框中动态加载配置管理编辑页面

/*以模态框的形式显示编辑页面*/
function doShowEditDialog(){
var url="config/editUI.do";
$("#myModal .modal-body")
.load(url,function(){
$("#myModalLabel").html("添加参数");
//show 显示
$("#myModal").modal('show');
});
}

说明:模态框添加在starter.html页面


5.配置管理数据添加实现

9.服务端实现

19.Dao实现

业务描述:

  1. 接收业务层数据(SysConfig)

  2. 将数据写入到数据库(ORM)

业务实现:在SysConfigDao接口中定义方法

  1. 方法名 insertObject

  2. 参数列表 SysConfig entity

  3. 返回值 int

关键代码实现:

int insertObject(SysConfig entity);

20.Mapper实现

业务描述

基于SysConfigDao中方法的定义编写SQL元素。

业务实现:

在SysConfigMapper文件中定义insert元素

  1. 元素名 insert

  2. 元素id insertObject

  3. Sql定义: insert into sys_configs (...) values(?,?,....)

关键代码实现:

<insert id="insertObject">
insert into sys_configs
(name,value,note,createdTime,
modifiedTime,createdUser,modifiedUser)
values
(#{name},#{value},#{note},now(),now(),
#{createdUser},#{modifiedUser})
</insert>

21.Service实现

业务描述

  1. 接收控制层数据(SysConfig对象)

  2. 对数据进行合法性验证(非空验证,..)

  3. 将对象通过Dao写入到数据库。

  4. 返回结果。

业务实现:在SysConfigService接口及实现类中定义保存方法

  1. 方法名 doSaveObject

  2. 参数列表 (SysConfig entity)

  3. 返回值 int

关键代码实现:

@Override
public int saveObject(SysConfig entity) {
//1.合法验证
if(entity==null)
throw new IllegalArgumentException("保存对象不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new IllegalArgumentException("参数名不能为空");
if(StringUtils.isEmpty(entity.getValue()))
throw new IllegalArgumentException("参数值不能为空");
//2.将数据写入到数据库
int rows;
try{
rows=sysConfigDao.insertObject(entity);
}catch(Throwable t){
t.printStackTrace();
//报警....
throw new ServiceException("系统故障,正在恢复");
}
//3.返回结果
return rows;
}

22.Controller实现

业务描述

  1. 接收客户端请求数据并将数据封装到SysConfig对象

  2. 调用业务层方法将数据写入数据库

  3. 对控制层数据进行封装(JsonResult),并返回

业务实现:在SysConfigController类定义保存数据方法

  1. 方法名 doSaveObject

  2. 参数列表 (SysConfig entity)

  3. 方法映射 doSaveObject

关键代码实现:

@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject(SysConfig entity){
sysConfigService.saveObject(entity);
return new JsonResult("save ok");
}

10.客户端实现

23.配置管理编辑页面实现

业务描述

  1. 模态框保存按钮点击事件注册

  2. 模态框隐藏事件注册

  3. 定义事件处理函数处理模态框的保存按钮事件。

业务实现:

  1. 在$(function(){})的回调函数中追加事件注册

  2. 定义保存按钮对应的事件处理函数doSaveOrUpdate

关键代码实现

模态框事件注册

在WEB-INF/pages/sys/config_edit.html中

$(function(){
$("#myModal").on("click",".btn-save",doSaveOrUpdate);
//注册模态框隐藏时的事件监听
$('#myModal').on('hidden.bs.modal', function (e) {
//移除myMoal对象上绑定的事件
//假如不移除有可能会重现表单重复提交
$("#myModal").off();
});
});

保存按钮的事件处理函数定义

function doSaveOrUpdate(){
//1.获取表单数据
var params=doGetFormData();
//2.定义url
var url="config/doSaveObject.do";
//2.将表单数据异步提交到服务端
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
$("#myModal").modal('hide');
doGetObjects();
} else{
alert(result.message);
}
});
}

点击保存按钮时获取表单数据的函数定义

function doGetFormData(){
var params={
"name":$("#nameId").val(),
"value":$("#valueId").val(),
"note":$("#noteId").val()
};
return params;
}

效果:


6.配置管理编辑页面数据呈现

11.服务端实现

24.Controller 实现

业务描述: 与添加操作共用显示编辑页面的方法。

关键代码:

/**
* 添加页面
*/
@RequestMapping("doConfigEditUI")
public String doConfigEditUI() {
return "sys/config_edit";
}

12.客户端实现

25,配置列表页面

业务描述:

  1. 修改按钮上进行事件注册(借助on方法)

  2. 修改按钮事件处理函数定义(与页面上的添加按钮共用是一个事件处理函数)

  3. 修改tbody中数据呈现的方法,在每个tr上绑定当前行记录。

业务实现:

  1. 在$(function(){})的事件处理函数中追加update按钮的事件注册操作

  2. 修改showEditDialog方法,基于点击对象的不同执行不同业务操作

  3. 在setTableBodyRows方法内部的tr对象上绑定当前行数据

关键代码实现

页面加载完成以后的事件处理

$(function(){
....     
//tbody中checkbox对象的事件注册
$("#tbodyId")
.on("click",".btn-update",doShowEditDialog)
});

修改按钮事件处理,在模态框中异步加载编辑页面。

function doShowEditDialog(){
var title;
//基于点击对象的类选择器判定是添加还是修改
if($(this).hasClass("btn-add")){
title="添加参数";
}else if($(this).hasClass("btn-update")){
title="修改参数";
//获取修改按钮对应的当前行记录
var rowData=
$(this).parents("tr").data("rowData");
//将当前行记录绑定到模态框上
//为什么要绑定到此对象?后续编辑页面可以直接从此对象直接获取数据
$("#myModal").data("rowData",rowData);
}
//异步加载编辑页面。
var url="config/editUI.do";
$("#myModal .modal-body").load(url,function(){
$("#myModalLabel").html(title);
$("#myModal").modal('show');
});
}

在列表页面数据呈现时的tr对象上绑定当前行记录对象。

function setTableBodyRows(records){
//1.获取tbody对象,并清空对象
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代records记录,并将其内容追加到tbody
for(var i in records){
//2.1 构建tr对象
var tr=$("<tr></tr>");
tr.data("rowData",records[i]);
//2.2 构建tds对象
var tds=createdTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}

26.配置编辑页面

业务描述

  1. 页面加载完成以后从模块框上获取绑定的数据。

  2. 通过模块框中绑定数据初始页页面表单。

业务实现:

  1. 在$(function(){})事件处理函数中追加获取绑定数据的操作

  2. 定义初始化页面表单数据的函数 doInitFormData();

关键代码实现:

$(function(){
....
//获取模态框绑定的数据
var rowData=$("#myModal").data("rowData");
if(rowData)doInitFormData(rowData);
});
function doInitFormData(rowData){
$("#nameId").val(rowData.name);
$("#valueId").val(rowData.value);
$("#noteId").val(rowData.note);
}

效果:


7.配置管理数据更新操作实现

13.服务端实现

27.Dao实现

业务描述

  1. 接收业务层数据(SysConfig对象)

  2. 将对象更新到数据库。

业务实现

1)在SysConfigDao接口中定义更新方法

a) 方法名 doUpdateObject

B) 参数列表 (SysConfig entity)

C) 返回值 int

关键代码实现:

/**
* 更新配置信息
* @param entity 封装配置信息
* @return 表示更新的行数
*/
int updateObject(SysConfig entity);

28.Mapper实现

业务描述

基于Dao中更新方法的定义编写sql更新元素

业务实现

在SysConfigMapper文件中定义更新元素

a.元素名 update

b.元素id updateObject

c.参数类型 (可选)

d.Sql定义 update sys_configs set .... where id=?

关键代码实现

<update id="updateObject">
update sys_configs
set
name=#{name},
value=#{value},
note=#{note},
modifiedTime=now(),
modifiedUser=#{modifiedUser}
where id=#{id}
</update>

29.Service实现

业务描述

  1. 对参数进行合法验证

  2. 通过dao将数据更新到数据库

  3. 对结果进行验证并返回。

业务实现:

在SysConfigService接口及实现类中定义业务处理方法

  1. 方法名 updateObject

  2. 参数列表 SysConfig entity

  3. 返回值 int

关键代码实现

@Override
public int updateObject(SysConfig entity) {
//1.合法性验证
if(entity==null)
throw new IllegalArgumentException("对象不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new IllegalArgumentException("参数名不能为空");
if(StringUtils.isEmpty(entity.getValue()))
throw new IllegalArgumentException("参数值不能为空");
//2.将数据更新到数据库
int rows=sysConfigDao.updateObject(entity);
//3.对结果进行验证
if(rows==0)
throw new ServiceException("此记录可能已经不存在");
//4.返回结果(会返回给谁?调用者)
return rows;
}

30.Controller实现

业务描述

  1. 接收客户端请求数据(借助SysConfig对象)

  2. 调用业务层方法更新数据

  3. 对结果进行封装(JsonResult)

  4. 将JsonResult对象转换为Json格式的字符串。

业务实现:

在SysConfigController中添加更新请求对应的处理方法

  1. 方法名 doUpdateObject

  2. 参数列表 (SysConfig entity)

  3. 返回值 JsonResult

  4. 方法映射 doUpdateObject

关键代码实现:

@RequestMapping("doUpdateObject")
@ResponseBody
public JsonResult doUpdateObject(SysConfig entity){
sysConfigService.updateObject(entity);
return new JsonResult("update ok");
}

14.客户端实现

31.编辑页面事件处理

业务描述

1)编辑页面上点击save按钮时异步更新数据

2)数据更新完成给出提示信息,隐藏模态框,刷新页面。

业务实现

修改doSaveOrUpdate函数,此函数内部基于模态框绑定的数据判定是添加还是修改。

关键代码实现:

更新操作事件处理函数定义。

/**
*模态框事件注册
*/
$(function(){
$("#myModal").on("click",".btn-save",doSaveOrUpdate);
//注册模态框隐藏时的事件监听
$('#myModal').on('hidden.bs.modal', function (e) {
//移除myMoal对象上绑定的事件
//假如不移除有可能会重现表单重复提交
$("#myModal").off();
//移除模太框绑定的数据
$("#myModal").removeData();
});
//获取模态框绑定的数据
var rowData = $("#myModal").data("rowData");
if(rowData) doInitFormData(rowData);
});
function doSaveOrUpdate(){
//1.获取表单数据
var params=doGetFormData();
var rowData=$("#myModal").data("rowData");
if(rowData)params.id=rowData.id;//修改时需要
//2.定义url
var insertUrl="config/doSaveObject.do";
var updateUrl="config/doUpdateObject.do";
var url=rowData?updateUrl:insertUrl;
//2.将表单数据异步提交到服务端
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
$("#myModal").modal('hide');
doGetObjects();
} else{
alert(result.message);
}
});
}

效果:

添加:

更新:

避免添加时有数据


8.总结

15.重点和难点分析

  1. 配置管理整体架构业务分析及实现。

  2. 配置管理持久层映射文件中SQL元素的定义及编写。

  3. 配置管理业务层业务分析,数据封装,异常抛出。

  4. 配置管理控制层请求数据映射,响应数据的封装及转换。

  5. 配置管理客户端JS代码,ajax异步请求响应的编写及调试。


16.常见FAQ

  1. 配置管理分页查询操作实现

1.1 服务端实现

  1. 配置管理分页数据加载的基本过程?(当前页数据获取,总记录数的获取)

  2. 映射文件中元素共性如何提取?(借助sql元素定义共性,使用include进行包含)

  3. 映射文件中参数的获取?(动态sql参数获取时,dao中的参数尽量使用@param修饰)

  4. 持久层只提供了Dao接口没有提供实现,请问实现类是谁创建的?(Spring)

  5. 配置管理中PageObject的作用是什么?(封装当前页面数据以及分页信息)

  6. 配置管理中JsonResult对象的作用是什么?(封装服务端的响应信息)

  7. 配置管理中的异常处理是如何实现的?(定义统一异常处理类,并对异常进行封装)

  8. 进行异常处理的目的是什么?(提高系统的容错能力,改善用户体验)

  9. 服务端控制层将对象转换为json串时使用的json库是什么?(jackson)

  10. Jackson库中的对象将Date对象转换到json串时默认是怎样的存储的?(long)

  11. 假如将对象转换为json串时,希望日期类型的对象按照自己指定格式进行输出,如何实现?(客户端转换,服务端转换)

  12. .....

1.2 客户端实现

  1. 多个异步请求的顺序问题?(两次异步请求需要有一定的先后顺序)

  2. getJSON函数的应用?(Get方式的ajax请求)

  3. JS对象的创建?(原生的,借助jquery)

  4. JS中的循环?(for(var i in records){})

  5. .....

2.配置管理删除操作实现?

  1. 服务端

  1. 删除方案?(业务层多次删除,数据层同时删除多个)

  2. 映射文件中动态Sql的编写(foreach)

  3. ....

2.客户端

  1. Checkbox值的获取(获取所有checkbox,然后判定选中,最后取选中的值)

  2. 全选操作的实现(业务,change事件处理)

  3. ....

     3.配置管理添加操作实现?

3.服务端

  1. 持久层数据添加操作的定义及实现

  2. 业务层数据保存业务的实现

  3. 控制层请求数据封装,处理,响应。

4.客户端

  1. 模态框的呈现及隐藏

  2. 模态框事件的绑定以及解绑

     4.配置管理修改操作实现?

5.服务端

  1. 持久层数据修改映射操作的定义及实现

  2. 业务层数据修改的实现

  3. 控制请求数据封装,处理,响应。

6.客户端

  1. 列表页面修改时当前行记录的获取以及绑定

  2. 列表页面修改与添加按钮共用一个事件处理函数,在模态框加载编辑页面。

  3. 编辑页面数据的初始化,点击修改按钮时数据数据的获取以及异步提交操作。


day03:京淘-权限管理子系统-菜单模块

1.    菜单页面呈现                                                            2
1.1.    服务端实现                                                       3
1.1.1.    菜单Controller实现                                  3
1.2.    客户端实现                                                        3
1.2.2.    定义菜单列表页面                                    3
1.2.3.    首页页面菜单管理事件处理                      3
2.    菜单页面数据呈现                                                        4
2.3.    服务端实现                                                          4
2.3.4.    DAO实现                                                   4
2.3.5.    Mapper实现                                               5
2.3.6.    Service实现                                               6
2.3.7.    Controller实现                                            7
2.4.    客户端实现                                                          7
2.4.8.    菜单列表页面实现                                     7
3.    菜单数据删除                                                             10
3.5.    服务端实现                                                         10
3.5.9.    DAO实现                                                  10
3.5.10.    Mapper实现                                            11
3.5.11.    Service实现                                            13
3.5.12.    Controller实现                                        14
3.6.    客户端实现                                                         15
3.6.13.    列表页面删除按钮事件处理                    15
4.    菜单添加页面呈现                                                       16
4.7.    服务端实现                                                         16
4.7.14.    菜单Controller方法定义                          16
4.8.    客户端实现                                                         17
4.8.15.    菜单编辑页面定义                                    17
4.8.16.    菜单列表页面添加按钮事件处理              17
5.    菜单编辑页面上级菜单呈现                                         18
5.9.    服务端实现                                                          18
5.9.17.    Node值对象                                            18
5.9.18.    Dao实现                                                  19
5.9.19.    Mapper实现                                             19
5.9.20.    Service实现                                              20
5.9.21.    Controller实现                                           20
5.10.    客户端实现                                                          21
5.10.22.    菜单编辑页面事件处理                             21
6.    菜单数据添加操作                                                           23
6.11.    服务端实现                                                           23
6.11.23.    Entity对象定义                                          23
6.11.24.    Dao 实现                                                 23
6.11.25.    Mapper中元素定义                                   24
6.11.26.    Service中方法定义                                24
6.11.27.    Controller实现                                        25
6.12.    客户端实现                                                        26
6.12.28.    保存按钮事件处理                                  26
7.    菜单修改页面数据呈现                                                  27
7.13.    服务端实现                                                         27
7.13.29.    Controller实现                                        27
7.14.    客户端实现                                                         28
7.14.30.    菜单列表页面修改按钮事件处理            28
7.14.31.    菜单编辑页面事件处理                           29
8.    菜单数据更新                                                                30
8.15.    服务端实现                                                         30
8.15.32.    Dao 实现                                                30
8.15.33.    Mapper 实现                                           30
8.15.34.    Service 实现                                           31
8.15.35.    Controller实现                                        32
8.16.    客户端实现                                                        32
8.16.36.    编辑页面保存按钮事件处理                   32
9.    总结                                                                              33
9.17.    重点和难点分析                                                 33
9.18.    常见FAQ                                                            34

  1. 菜单页面呈现

核心业务

点击系统首页中的菜单管理, 在对应的一个div中

异步加载菜单页面.


  1. 服务端实现

    1. 菜单Controller实现

业务描述

  1. 定义controller对象处理客户端菜单访问请求

  2. 定义相关方法处理列表页面加载需求

业务实现(定义一个Controller类)

  1. 包名 com.jt.sys.controller

  2. 类名 SysMenuController

  3. 映射 @RequestMapping("/menu/")

业务实现(基于Controller类定义页面响应方法)

  1. 方法名 doMenuListUI

  2. 参数列表()

  3. 返回值 String

  4. 映射 @RequestMapping("doMenuListUI")

代码实现:

@RequestMapping("doMenuListUI")
public String doMenuListUI(){
return "sys/menu_list";
}

2.客户端实现

2.定义菜单列表页面

在WEB-INF/pages/sys目录下定义menu_list.html页面.


3.首页页面菜单管理事件处理

业务描述

1)在starter.html页面中注册菜单管理的点击事件

2)在starter.html页面中定义事件处理函数,异步加载菜单列表页面

代码实现:

$(function(){
doLoadUI("#load-menu-id"," menu/doMenuListUI.do");
});
function doLoadUI (domId,url){
$(domId).click(function(){
//异步加载url对应的资源,并将资源插入到.container-fluid位置
$("#mainContentId").load(url,function(){
$("#mainContentId").removeData();
});
});
}

2.菜单页面数据呈现

3.服务端实现

核心业务

从数据库查询菜单以及上一级的菜单信息,

并数据信息进行封装(本模块封装到map),传递.


4.DAO实现

业务描述

  1. 定义菜单持久层对象,处理数据访问操作

  2. 定义菜单查询方法,查询所有菜单以及上一级菜单信息(只取id,名字)

业务实现:(持久层接口定义)

  1. 包名: com.jt.sys.dao

  2. 类名: SysMenuDao

业务实现:(接口方法定义)

  1. 方法名 findObjects  (本查询不做分页)

  2. 参数列表()

  3. 返回值 List<Map<String,Object>> (一行记录对应一个map,多个map放list)

代码实现:

public interface SysMenuDao {
List<Map<String,Object>> findObjects();
}

5.Mapper实现

业务描述

基于SysMenuDao创建SysMenuMapper文件,并在文件中

基于SysMenuDao的查询方法定义SQL映射元素

业务实现()

  1. 元素名  select

  2. 元素id  findObjects

  3. 结果类型 resultType=map

  4. SQL定义(关联查询本菜单以及上一级菜单相关信息)

代码实现:

<select id="findObjects"
resultType="map">
<!-- 方案1
select c.*,p.name parentName
from sys_menus c left join sys_menus p
on c.parentId=p.id
-->
<!-- 方案2 -->
select c.*,(
select p.name
from sys_menus p
where c.parentId=p.id
) parentName
from sys_menus c
</select>

6.Service实现

业务描述

  1. 定义菜单业务接口,负责处理菜单模块业务

  2. 定义业务方法,访问dao层方法获取菜单信息

业务实现 (菜单接口定义)

  1. 包名: com.jt.sys.service

  2. 类名: SysMenuService

业务实现(菜单业务接口方法定义)

  1. 方法名 findObjects

  2. 参数列表()

  3. 返回值 List<Map<String,Object>>

代码实现:

public interface SysMenuService {
List<Map<String,Object>> findObjects();
}

业务实现(菜单业务实现类定义)

  1. 包名: com.jt.sys.service.impl

  2. 类名:SysMenuServiceImpl

代码实现:

@Service
public class SysMenuServiceImpl implements SysMenuService {
@Autowired
private SysMenuDao sysMenuDao;
@Override
public List<Map<String, Object>> findObjects() {
return sysMenuDao.findObjects();
}
}

7.Controller实现

业务描述

1)基于客户端请求,借助业务层对象访问菜单信息

2)对菜单信息进行封装,并返回.

业务实现(SysMenuController中方法定义)

  1. 方法名 doFindObjects

  2. 参数列表()

  3. 返回值 JsonResult

  4. 映射@RequestMapping("doFindObjects")

代码实现:

@Controller
@RequestMapping("/menu/")
public class SysMenuController {
@Autowired
private SysMenuService sysMenuService;
@RequestMapping("doFindObjects")
@ResponseBody
public JsonResult doFindObjects(){
return new JsonResult(sysMenuService.findObjects());
}
}

4.客户端实现

8.菜单列表页面实现

业务描述

  1. 数据呈现时使用jquery中的treeGrid插件.

  2. 页面加载完成,异步加载数据,以树结构table形式呈现.

业务实现

  1. 引入treeGrid相关js文件

  2. 定义方法异步加载数据

代码实现 menu_list.html文件

var columns = [
{
field : 'selectItem',
radio : true
},
{
title : '菜单ID',
field : 'id',
visible : false,
align : 'center',
valign : 'middle',
width : '80px'
},
{
title : '菜单名称',
field : 'name',
align : 'center',
valign : 'middle',
sortable : true,
width : '130px'
},
{
title : '上级菜单',
field : 'parentName',
align : 'center',
valign : 'middle',
sortable : true,
width : '130px'
},
{
title : '类型',
field : 'type',
align : 'center',
valign : 'middle',
sortable : true,
width : '70px',
formatter : function(item, index) {
if (item.type == 1) {
return '<span class="label label-success">菜单</span>';
}
if (item.type == 2) {
return '<span class="label label-warning">按钮</span>';
}
}
},
{
title : '排序号',
field : 'sort',
align : 'center',
valign : 'middle',
sortable : true,
width : '70px'
},
{
title : '菜单URL',
field : 'url',
align : 'center',
valign : 'middle',
sortable : true,
width : '160px'
},
{
title : '授权标识',
field : 'permission',
align : 'center',
valign : 'middle',
sortable : true
} ];
$(document).ready(function(){
doGetObjects();
}

定义页面数据加载方法

function doGetObjects(){
//构建table对象
var table=new TreeTable(
"menuTable", //tableId
"menu/doFindObjects.do", //url
columns);//columns
//设置从哪一列展开内容(默认是第一列)
table.setExpandColumn(2);//可以不写
//初始化table对象(底层发ajax异步请求访问服务端)
table.init();
}

效果:


3.菜单数据删除

核心业务

  1. 查询此菜单有没有对应的子菜单,有则不允许删除

  2. 删除菜单(市场有两种业务)

        a.有角色菜单关系数据,则不允许删除

        b.有角色菜单关系数据,将关系数据一起删除.(本次策略)


5.服务端实现

9.DAO实现

业务实现:

  1. 基于菜单id查询是否有子菜单(有子菜单则不能删除)

  2. 根据菜单id删除菜单自身信息.

  3. 根据菜单id删除角色菜单关系数据

业务实现

1.在SysMenuDao接口中定义基于id查询是否有子菜单的方法)

        a.方法名 getChildCount

        b.参数列表 (Integer id)

        c.返回值 int

2.在SysMenuDao接口中定义根据id删除菜单的方法

        a.方法名 deleteObject

        b.参数列表 (Integer id)

        c.返回值 int

3.创建基于角色菜单关系表的DAO接口

        a.包名: com.jt.sys.dao

        b.接口名: SysRoleMenuDao

4.在SysRoleMenuDao接口中创建基于菜单id删除记录的方法

        a.方法名: deleteObjectsByMenuId

        b.参数列表 (Integer menuId)

        c.返回值 int

关键代码实现

SysMenuDao 接口方法定义

/**
* 根据菜单id统计子菜单的个数
* @param id
* @return
*/
int getChildCount(Integer id);
/**
* 根据id 删除菜单
* @param id
* @return
*/
int deleteObject(Integer id);

SysRoleMenuDao接口及方法定义

public interface SysMenuDao {
int deleteObjectsByMenuId(Integer menuId);
}

10.Mapper实现

业务描述

基于SysMenuDao中方法定义映射元素.

业务实现:

1.SysMenuMapper中统计子菜单元素定义

    a.元素名 select

    b.元素id getChildCount

    c.参数类型 int

    d.结果类型 int

    e.SQL定义 (select count(*) from sys_menus where parentId=#{id})

2.SysMenuMapper根据id删除菜单的元素定义

    a.元素名 delete

    b.元素id deleteObject

    c.参数类型 int

    d.SQL定义 (delete from sys_menus where id=#{id})

3.创建基于SysRoleMenuDao的映射文件

    a.包名:mapper.sys

    b.文件名SysRoleMenuMapper

4.SysRoleMenuMapper文件中定义基于菜单id删除记录的元素

    a.元素名 delete

    b.元素id deleteObjectsByMenuId

    c.参数类型 int

    d.SQL定义 (delete from sys_role_menus where menu_id=#{menuId})

关键代码实现

SysMenuDao根据id统计子菜单

<select id="getChildCount"
parameterType="int"
resultType="int">
select count(*)
from sys_menus
where parentId=#{id}       
</select>

SysMenuDao根据id删除菜单

<delete id="deleteObject"
parameterType="int">
delete from sys_menus
where id=#{id}
</delete>

SysRoleMenuDao根据菜单id删除记录

<delete id="deleteObjectsByMenuId"
parameterType="int">
delete from sys_role_menus
where menu_id=#{id}
</delete>

11.Service实现

业务描述

  1. 接收控制数据(id),并对数据进行合法验证

  2. 基于id查询菜单子元素,并进行判定

  3. 删除菜单元素

  4. 删除角色菜单关系数据.

  5. 返回结果.

业务实现(在SysMenuService及实现类中定义方法)

  1. 方法名 deleteObject

  2. 参数列表 (Integer id)

  3. 返回值 int

代码实现

接口及方法定义

public interface SysMenuService {
int deleteObject(Integer id);
}

实现类中方法实现

@Service
public class SysMenuServiceImpl implements SysMenuService {
@Autowired
private SysMenuDao sysMenuDao;
@Autowired
private SysRoleMenuDao sysRoleMenuDao;
@Override
public int deleteObject(Integer id) {
//1.验证数据的合法性
if(id==null||id<=0)
throw new ServiceException("请先选择");
//2.基于id进行子元素查询
int count=sysMenuDao.getChildCount(id);
if(count>0)
throw new ServiceException("请先删除子菜单");
//3.删除菜单元素
int rows=sysMenuDao.deleteObject(id);
if(rows==0)
throw new ServiceException("此菜单可能已经不存在");
//4.删除角色,菜单关系数据
sysRoleMenuDao.deleteObjectsByMenuId(id);
//5.返回结果
return rows;
}
}

12.Controller实现

业务描述

  1. 获取客户端请求数据(菜单id)

  2. 调用业务层方法删除数据

  3. 封装并返回结果

业务实现(在SysMenuController中定义相关方法)

  1. 方法名 doDeleteObject

  2. 参数列表 Integer id

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doDeleteObject")

代码实现

@Controller
@RequestMapping("/menu/")
public class SysMenuController {
@Autowired
private SysMenuService sysMenuService;
@RequestMapping("doDeleteObject")
@ResponseBody
public JsonResult doDeleteObject(Integer id){
sysMenuService.deleteObject(id);
return new JsonResult("delete OK");
}
......
}

6.客户端实现

13.列表页面删除按钮事件处理

业务描述

点击删除按钮时向服务端提交异步请求,删除用户选中的菜单记录.

业务实现:

  1. 删除按钮事件注册

  2. 删除按钮事件处理函数定义.

  /WEB-INF/pages/sys/menu_list.html代码实现:

$(document).ready(function(){
....
$(".input-group-btn")
.on("click",'.btn-delete',doDeleteObject)
});

执行删除操作

function doDeleteObject(){
//1.获取选中的记录id
var id=getCheckedId();
if(!id){
alert("请先选择");
return;
}
//2.异步提交请求删除数据
var url="menu/doDeleteObject.do";
var params={"id":id};
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doGetObjects();
}else{
alert(result.message);
}
});
}

获取选中记录的id值

function getCheckedId(){
//1.获取选中的记录
var selections=$("#menuTable")
//bootstrapTreeTable是treeGrid插件内部定义的jquery扩展函数
//getSelections为扩展函数内部要调用的一个方法
.bootstrapTreeTable("getSelections");
//2.对记录进行判定
if(selections.length==1)
return selections[0].id;
}

效果:

4.菜单添加页面呈现

7.服务端实现

14.菜单Controller方法定义

业务描述

基于客户端的添加请求返回一个响应页面

业务实现(在SysMenuController中添加相关方法)

  1. 方法名 doMenuEditUI

  2. 参数列表 ()

  3. 返回值 String

  4. 映射 @RequestMapping("doMenuEditUI")

代码实现:

@RequestMapping("doMenuEditUI")
public String doMenuEditUI(){
return "sys/menu_edit";
}

8.客户端实现

15.菜单编辑页面定义

在WEB-INF/pages/sys目录中添加menu_edit.html.

说明:已有就不用添加了.


16.菜单列表页面添加按钮事件处理

业务描述

点击添加按钮异步访问服务器,加载对应菜单编辑页面.

业务实现

  1. 事件注册

  2. 事件处理函数定义

代码实现

/WEB-INF/pages/sys/menu_list.html事件注册

$(document).ready(function(){
...   
$(".input-group-btn")
.on("click",".btn-add",doLoadEditUI);
});

加载页面

function doLoadEditUI(){
var title;
if($(this).hasClass("btn-add")){
title="添加菜单"
}
var url="menu/doMenuEditUI.do";
$(“ #mainContentId").load(url,function(){
$(".box-title").html(title);
})
}

效果:

5.菜单编辑页面上级菜单呈现

核心业务

  1. 点击编辑页面的上级菜单时向服务端发起异步请求

  2. 服务端基于请求获取菜单节点数据

  3. 客户端通过服务端返回数据异步刷新页面.


9.服务端实现

17.Node值对象

业务描述

定义一个Node对象,借助此对象封装从数据库查询到的数据

业务实现 (类的定义)

  1. 包名 com.jt.common.vo

  2. 类名 Node

  3. 接口 实现序列化接口并添加版本id

  4. 字段 (Integer id,String name,Integer parentId) 提供set/get方法

代码实现:

public class Node implements Serializable{
private static final long serialVersionUID = -6577397050669133046L;
private Integer id;
private String name;
private Integer parentId;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
@Override
public String toString() {
return "Node [id=" + id + ", name=" + name + ", parentId=" + parentId + "]";
}
}

18.Dao实现

业务描述

基于请求获取数据库对应的菜单表中的所有菜单信息(id,name,parentId)

业务实现(SysMenuDao中方法定义)

  1. 方法名 findZtreeMenuNodes

  2. 参数列表()

  3. 返回值 List<Node>

代码实现:

List<Node> findZtreeMenuNodes();


19.Mapper实现

业务描述

基于SysMenuDao中方法定义菜单节点查询元素.

业务实现(SysMenuMapper中元素定义)

  1. 元素名 select

  2. 元素id findZtreeMenuNodes

  3. 结果类型 com.jt.common.vo.Node

  4. SQL 定义 (select id,name,parentId from sys_menus)

代码实现:

<select id="findZtreeMenuNodes" resultType="com.jt.common.vo.Node">
select id,name,parentId
from sys_menus
</select>

20.Service实现

业务描述

基于客户端请求,借助dao对象,访问菜单节点信息,并返回.

业务实现(SysMenuService接口及实现类中方法定义)

  1. 方法名 findZtreeMenuNodes

  2. 参数列表()

  3. 返回值 List<Node>

代码实现.

@Override
public List<Node> findZtreeMenuNodes() {
return sysMenuDao.findZtreeMenuNodes();
}

21.Controller实现

业务描述

基于客户端请求,访问业务层对象方法,获取菜单节点对象,并封装返回.

业务实现(SysMenuController中方法定义)

  1. 方法名 doFindZtreeMenuNodes

  2. 参数列表()

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doFindZtreeMenuNodes");

代码实现:

@RequestMapping("doFindZtreeMenuNodes")
@ResponseBody
public JsonResult doFindZtreeMenuNodes(){
return new JsonResult(
sysMenuService.findZtreeMenuNodes(),
"query ok");
}

10.客户端实现

22.菜单编辑页面事件处理

业务描述

点击上级菜单时,异步加载菜单节点信息,并进行数据呈现.

业务实现

  1. 点击事件注册

  2. 事件处理函数定义

 WEB-INF/pages/sys/menu_edit.html

代码实现:

var zTree;
//初始化zTree时会用到
var setting = {
data : {
simpleData : {
enable : true,
idKey : "id",  //节点数据中保存唯一标识的属性名称
pIdKey : "parentId",  //节点数据中保存其父节点唯一标识的属性名称
rootPId : null  //根节点id
}
}
}

页面加载完成,异步加载数据,事件注册

$(document).ready(function(){
$(".form-horizontal")
.on("click",".load-sys-menu",doLoadZtreeNodes);//初始化树结构
$("#menuLayer")
.on("click",".btn-confirm",doSetSelectNode)//确定 获取选中的节点
.on("click",".btn-cancel",doHideTree)//取消
});

设置选中的节点对象(这里坑过)

function doSetSelectNode(){
//1.获取选中的节点对象
var nodes=zTree.getSelectedNodes();
if(nodes.length==0) {
alert("请选中"); 
return;
}
var node=nodes[0];
console.log(node);
//2.将对象中内容,填充到表单
$("#parentId").data("parentId",node.id);
$("#parentId").val(node.name);
//3.隐藏树对象
doHideTree();
}

点击树div的确定和取消按钮时隐藏树对象

function doHideTree(){
$("#menuLayer").css("display","none");
}

加载树节点信息,并初始化树结构 

function doLoadZtreeNodes(){
//显示div
$("#menuLayer").css("display","block");
var url="menu/doFindZtreeMenuNodes.do";
//异步加载数据,并初始化数据
$.getJSON(url,function(result){
if(result.state==1){
//使用init函数需要先引入ztree对应的js文件
zTree=$.fn.zTree.init(
$("#menuTree"),
setting,
result.data);
}else{
alert(result.message);
}
})
}

效果:


 6.菜单数据添加操作

11.服务端实现

核心业务描述

点击页面保存按钮时,获取菜单表单数据,异步提交到服务器端。


23.Entity对象定义

业务描述

创建实体类封装客户端请求中的的菜单数据

类的定义

  1. 包名:com.jt.sys.entity

  2. 类名:SysMenu

类中属性及方法定义

  1. 实现序列化接口并添加序列化版本id

  2. 属性与表(sys_menus)中字段有对应关系

  3. 提供set/get方法,无参数构造方法,重写toString方法

public class SysMenu extends BaseEntity implements Serializable{
private static final long serialVersionUID = -5259265803332215029L;
/**菜单id*/
private Integer id;
/**菜单名*/
private String name;
/**菜单对应的url*/
private String url;
/**菜单类型*/
private Integer type;
/**菜单的排序号*/
private Integer sort;
/**备注*/
private String note;
/**菜单的父id*/
private Integer parentId;
/**权限标识*/
private String permission;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
}

24.Dao 实现

业务描述

在SysMenuDao中添加插入数据的方法,用于将菜单信息写入到数据库

业务实现:

SysMenuDao接口方法定义

  1. 方法名 insertObject

  2. 返回值 int

  3. 参数列表 (SysMenu entity)

方法定义

    int insertObject(SysMenu entity);


25.Mapper中元素定义

业务描述

基于SysMenuDao中方法定义SQL映射元素。

业务实现

在SysMenuMapper中定义insert元素

  1. 元素名 insert

  2. 元素id insertObject

  3. 参数类型 com.jt.sys.entity.SysMenu

  4. Sql 定义 insert into …

关键代码实现 

<insert id="insertObject"    
parameterType="com.jt.sys.entity.SysMenu">
insert into sys_menus
(name,url,type,sort,note,parentId,permission,
createdTime,modifiedTime,createdUser,modifiedUser)
values
(#{name},#{url},#{type},#{sort},#{note},#{parentId},
#{permission},now(),now(),#{createdUser},
#{modifiedUser})
</insert>

26.Service中方法定义

业务描述

  1. 获取页面中数据,并对数据进行合法验证

  2. 将数据写入到数据库

  3. 判定结果,并返回

业务实现:

在SysMenuService接口及实现类添加相关方法)

  1. 方法名saveObject

  2. 返回值int

  3. 参数列表 (SysMenu entity)

关键代码实现

@Override
public int saveObject(SysMenu entity) {
//1.合法验证
if(entity==null)
throw new ServiceException("保存对象不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new ServiceException("菜单名不能为空");
int rows;
//2.保存数据
try{
rows=sysMenuDao.insertObject(entity);
}catch(Exception e){
e.printStackTrace();
throw new ServiceException("保存失败");
}
//3.返回数据
return rows;
}

27.Controller实现

业务描述

  1. 获取客户端请求数据

  2. 调用业务层方法将数据写入数据库

  3. 封装结果并返回

方法定义(在SysMenuController中添加相关方法)

  1. 方法名 doSaveObject

  2. 返回值 JsonResult

  3. 参数列表(SysMenu entity)

  4. url 映射(doSaveObject)

代码实现

@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject(SysMenu entity){
sysMenuService.saveObject(entity);
return new JsonResult("save ok");
}

12.客户端实现

28. 保存按钮事件处理

业务描述

  1. 编辑页面save按钮事件注册及事件处理函数定义

  2. 编辑页面Cancel按钮事件注册及事件处理函数定义

业务实现

  1. 按钮点击事件注册

  2. 事件处理函数定义

$(document).ready(function(){


//初始化树结构  
$(".form-horizontal")
.on("click",".load-sys-menu",doLoadZtreeNodes);


//表单提交按钮和取消按钮        
$(".box-footer")
.on("click",".btn-cancel",doCancel)
.on("click",".btn-save",doSaveOrUpdate);


//选择菜单的确定和取消按钮的注册
$("#menuLayer")
.on("click",".btn-cancel",doHideTree)
.on("click",".btn-confirm",doSetSelectNode)

//获取#mainContentId上绑定的数据
var rowData=$("#mainContentId").data("rowData");
if(rowData)doInitEditFormData(rowData);
});

代码实现

Cancel 按钮事件处理函数定义

function doCancel(){
$("#mainContentId").load("menu/doMenuListUI.do",function(){
$("#mainContentId").removeData();
$("#parentId").removeData();
});
}

Save按钮事件处理函数定义

function doSaveOrUpdate(){
//1.获取表单数据
var params= doGetEditFormData();
//2.定义url
var url="menu/doSaveObject.do";
//3.异步提交数据
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
//加载列表页面
doCancel();
}else{
alert(result.message);
}
});
}

获取表单数据

function doGetEditFormData(){
var params={
type:$("form input[name='typeId']:checked").val(),
name:$("#nameId").val(),
url:$("#urlId").val(),
sort:$("#sortId").val(),
permission:$("#permissionId").val(),
parentId:$("#parentId").data("parentId")
}
return params;
}

效果:

点击添加:

 

 

 

7.菜单修改页面数据呈现

13.服务端实现

29.Controller实现

业务描述

基于客户端的添加请求返回一个响应页面(与添加操作共用一个页面)

业务实现(在SysMenuController中添加相关方法)

5.方法名 doMenuEditUI

6.参数列表 ()

7.返回值 String

8.映射 @RequestMapping("doMenuEditUI")

代码实现:

@RequestMapping("doMenuEditUI")
public String doMenuEditUI(){
return "sys/menu_edit";
}


14.客户端实现

30.菜单列表页面修改按钮事件处理

业务描述

点击列表页面修改按钮时加载编辑页面

WEB-INF/pages/sys/menu_list.html

业务实现

  1. 事件注册

$(document).ready(function(){
doGetObjects();
$(".input-group-btn")
//绑定删除按钮
.on("click",".btn-delete",doDeleteObject)
//绑定添加按钮,绑定修改按钮
.on("click",".btn-add .btn-update",doLoadEditUI);
});
  1. 事件处理(绑定id,设置标题,异步加载页面)

代码实现:

  1. 页面加载方法定义

function doLoadEditUI(){
var title;
if($(this).hasClass("btn-add")){
title="添加菜单"
}else if($(this).hasClass("btn-update")){
title="修改菜单"
//获取选中的记录数据
var rowData=getCheckedItem();
if(!rowData){
alert("请选择一个");
return;
}
$(" #mainContentId").data("rowData",rowData);
}
var url="menu/doMenuEditUI.do";
$("#mainContentId").load(url,function(){
$(".box-title").html(title);
})
}

获取tr对象上绑定的数据(需要修改源代码找到treegridtr对象创建的地方然后追加绑定数据的操作。)

jquery源码:

function getCheckedItem(){
var tr=$("tbody input[type='radio']:checked")
.parents("tr");
return tr.data("rowData");
}

31.菜单编辑页面事件处理

WEB-INF/pages/sys/menu_edit.html

页面描述

页面加载完成,从div中获取绑定的对象,通过对象数据初始化页面表单

方法定义

  1. doInitEditForm(data); 通过查找获取的数据初始化表单

代码实现:

页面加载完成时获取绑定的数据

$(document).ready(function(){
//初始化树结构
$(".form-horizontal").on("click",".load-sys-menu",doLoadZtreeNodes);
//选择菜单的确定和取消按钮的注册
$("#menuLayer")
.on("click",".btn-cancel",doHideTree)
.on("click",".btn-confirm",doSetSelectNode);
//表单提交按钮和取消按钮
$(".box-footer")
.on("click",".btn-cancel",doCancel)
.on("click",".btn-save",doSaveOrUpdate);     
//获取#mainContentId上绑定的数据
var data=$("#mainContentId").data("rowData");
if(data) doInitFormData(data);
});

编辑页面加载完成执行此方法

function doInitFormData(data){
/*   $("input[type='radio']").each(function(){
if($(this).val()==data.type){
$(this).prop("checked",true);
}
}) */
$("#form").data("id",data.id);
$(".typeRadio input[value='"+data.type+"']").prop("checked",true);
$("#nameId").val(data.name);
$("#sortId").val(data.sort);
$("#urlId").val(data.url);
$("#permissionId").val(data.permission);
$("#parentId").val(data.parentName);
$("#parentId").data("parentId",data.parentId);
}

效果:

此处当时的bug

巨坑啊,老齐居然改了jQuery源代码

渲染行将item的值绑定到tr上的rowData上


8.菜单数据更新

15.服务端实现

32.Dao 实现

业务描述

在SysMenuDao中添加修改菜单方法,用于实现数据库中菜单信息的修改

方法定义

  1. 方法名 updateObject

  2. 返回值 int

  3. 参数列表 (SysMenu entity)

代码实现

int updateObject(SysMenu entity);

33.Mapper 实现

业务描述

SysMenuMapper中添加修改菜单数据的元素

元素定义

  1. 元素名 update

  2. 元素id updateObject

  3. 参数 com.jt.sys.entity.SysMenu

  4. Sql 定义 update sys_menus set  where ….

代码实现:

<update id="updateObject"
parameterType="com.jt.sys.entity.SysMenu">
update sys_menus
set
name=#{name},
type=#{type},
sort=#{sort},
url=#{url},
parentId=#{parentId},
permission=#{permission},
modifiedUser=#{modifiedUser},
modifiedTime=now()
where id=#{id}
</update>

34.Service 实现

业务描述

  1. 获取控制层数据,对数据进行合法性验证

  2. 通过持久层对象将数据更新到数据库

  3. 验证并返回结果

方法定义

  1. 方法名 updateObject

  2. 返回值 int

  3. 参数列表(SysMenu entity)

代码实现

@Override
public int updateObject(SysMenu entity) {
//1.合法验证
if(entity==null)
throw new ServiceException("保存对象不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new ServiceException("菜单名不能为空");
//2.更新数据
int rows=sysMenuDao.updateObject(entity);
if(rows==0)
throw new ServiceException("记录可能已经不存在");
//3.返回数据
return rows;
}

35.Controller实现

业务描述(SysMenuController)

  1. 接收客户端数据

  2. 将数据通过业务层对象进行更新操作

  3. 封装结果并返回

方法定义

  1. 方法名 doUpdateObject

  2. 参数列表 SysMenu entity

  3. 返回值 JsonResult

  4. url 映射 doUpdateObject

@RequestMapping("doUpdateObject")
@ResponseBody
public JsonResult doUpdateObject(SysMenu entity){
sysMenuService.updateObject(entity);
return new JsonResult("update ok");
}

16.客户端实现

36.编辑页面保存按钮事件处理

业务描述

  1. 保存按钮事件注册

  2. 事件处理函数定义(与添加方法共用一个函数)

代码实现:

function doSaveOrUpdate(){
//1.获取表单数据
var params=getEditFormData();
var id=$("#form").data("id");
//2.定义url
var insertUrl="menu/doSaveObject.do";
var updateUrl="menu/doUpdateObject.do";
var url=id?updateUrl:insertUrl;
if(id)params.id=id;
//3.异步提交数据
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
});
}
function getEditFormData(){
var params={
type:$("form input[name='typeId']:checked").val(),
name:$("#nameId").val(),
url:$("#urlId").val(),
sort:$("#sortId").val(),
permission:$("#permissionId").val(),
parentId:$("#parentId").data("parentId")
}
return params;
}

效果:

(略)


9.总结

17.重点和难点分析

  1. 菜单管理在整个系统的中的角色定位

  2. 菜单管理表设计以及自关联查询设计

  3. 多表查询结果的映射问题

  4. 菜单数据的客户端展示(zTree,TreeGrid)


18.常见FAQ


day04:京淘项目-权限管理子系统-角色模块

1.    角色列表页面呈现                                                2

1.1.    服务端实现                                               2

1.1.1.    Controller实现                                     2

1.2.    客户端实现                                               3

1.2.2.    定义角色列表页面                                    3

1.2.3.    首页页面菜单事件处理                                 3

1.3.    角色分页div内容加载                                       4

2.    角色列表数据呈现                                                4

2.4.    服务端实现                                               4

2.4.4.    Entity类实现                                       4

2.4.5.    Dao接口实现                                        5

2.4.6.    Mapper实现                                         6

2.4.7.    Service层对象                                      7

2.4.8.    Controller 实现                                    9

2.5.    客户端实现                                              10

2.5.9.    角色页面数据呈现                                   10

2.5.10.    列表页面role_list.html信息查询实现                 12

3.    角色删除模块实现                                               14

3.6.    服务端实现                                              14

3.6.11.    DAO实现                                          14

3.6.12.    Mapper实现                                       15

3.6.13.    Service实现                                      16

3.6.14.    Controller实现                                   17

3.7.    客户端实现                                              17

3.7.15.    角色列表页面删除操作实现                           17

4.    角色添加页面呈现                                               18

4.8.    服务端实现                                              18

4.8.16.    角色Controller对象的实现                          18

4.9.    客户端实现                                              19

4.9.17.    角色列表添加按钮事件处理                           19

4.9.18.    角色添加页面菜单信息呈现                           20

5.    角色添加操作实现                                               21

5.10.    服务端实现                                             21

5.10.19.    Dao实现                                         21

5.10.20.    Mapper实现                                      22

5.10.21.    Service实现                                     23

5.10.22.    Controller实现                                  24

5.11.    客户端实现                                             25

5.11.23.    角色编辑页面按钮事件处理                          25

6.    角色修改页面呈现                                               26

6.12.    服务端实现                                             26

6.12.24.    Dao实现                                         26

6.12.25.    Mapper实现                                      27

6.12.26.    Service实现                                     28

6.12.27.    Controller实现                                  29

6.13.    客户端实现                                             29

6.13.28.    角色列表页面实现                                 29

6.13.29.    角色编辑页面实现                                 31

7.    角色修改操作实现                                               32

7.14.    服务端实现                                             32

7.14.30.    Dao实现                                         32

7.14.31.    Mapper实现                                      33

7.14.32.    Service实现                                     34

7.14.33.    Controller实现                                  35

7.15.    客户端实现                                             35

7.15.34.    角色编辑页面数据的更新                            35

8.    总结                                                          37

8.16.    重点和难点分析                                          37

8.17.    常见FAQ                                               38


  1. 角色列表页面呈现

    1. 服务端实现

      1. Controller实现

业务描述

创建角色控制层对象,并添加相关返回具体view.

页面实现

1.Controller类的创建

   a.包名 com.jt.sys.controller

   b.类名 SysRoleController

   c.映射 @RequestMapping("/role/")

2.Controller 类中方法定义,并返回role_list页面

     a.方法名 doRoleListUI

     b.参数列表()

     c.返回值 String

     d.映射 @RequestMapping("doRoleListUI")

代码实现:

@Controller
@RequestMapping("/role/")
public class SysRoleController {
@RequestMapping("doRoleListUI")
public String doRoleListUI(){
return "sys/role_list";
}
}

2.客户端实现

2.定义角色列表页面

在WEB-INF/pages/sys目录下定义role_list.html页面.


3.首页页面菜单事件处理

业务描述

1)在starter.html页面中注册角色管理的点击事件

2)在starter.html页面中定义事件处理函数,异步加载菜单列表页面

业务实现

  1. 在$(function(){})追加事件处理函数的调用loadUI()

关键代码实现:

$(function(){
doLoadUI(“#load-role-id","role/doRoleListUI.do");
});

3.角色分页div内容加载

页面描述

角色列表页面加载完成异步加载分页页面

业务实现

在role_list.html页面底部添加JS异步加载分页页面的实现

关键代码实现

$(function(){
$("#pageId").load("doPageUI.do");
})

效果:


 2.角色列表数据呈现

4.服务端实现

4.Entity类实现

业务描述:

定义实体封装从数据库查询的数据

业务实现

构建与sys_roles表对应的实体类型

  1. 包名 com.jt.sys.entity

  2. 类名 SysConfig (实现序列化接口,并定义序列化id)

  3. 属性 与表(sys_configs)中字段有对应关系

  4. 方法 set/get

关键代码实现:

public class SysRole implements Serializable{
private static final long serialVersionUID = 3098457856539501697L;
private Integer id;
private String name;
private String note;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
....
}

5.Dao接口实现

业务描述:(核心-查询当前页显示的数据以及总记录数)

  1. 接收业务层参数数据

  2. 基于参数进行数据查询

  3. 将查询结果进行封装

  4. 将结果返回给业务层对象

业务实现:创建Dao接口并定义相关方法。

1.Dao接口定义

   a.包名: com.jt.sys.dao

   b.名字: SysRoleDao

2.方法定义:负责基于条件查询当前页数据

   a.方法名:findPageObjects

   b.参数列表:(String name,Integer startIndex,Integer pageSize)

   c.返回值:List<SysConfig>

3.方法定义:负责基于条件查询总记录数

    a.方法名:getRowCount

    b.参数列表:(String name)

     c.返回值:int

思考:DAO中方法的参数在mapper中如何获取?

代码实现:

public interface SysRoleDao {
/**
* 分页查询角色信息
* @param startIndex 上一页的结束位置
* @param pageSize 每页要查询的记录数
* @return
*/
List<SysRole> findPageObjects(
@Param("name")Integer name,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize);
/**
* 查询记录总数
* @return
*/
int getRowCount(@Param("name")String name);
}

6.Mapper实现

业务描述

  1. 基于Dao接口创建映射文件

  2. 基于Dao方法在映射文件中创建映射元素建映射元素

业务实现:

1.创建映射文件

    a.包名:mapper.sys

    b.文件名:SysRoleMapper.xml

    c.命名空间 com.jt.sys.dao.SysRoleDao

2.创建映射元素实现翻页查询操作

     a.元素名  select

     b.元素id  findPageObjects

      c.参数类型 (不写)

      d.结果类型 com.jt.sys.entity.SysRole

      e.SQL定义 select * from sys_roles where name like ? limit ?,?

3.创建映射元素实现查询统计操作

      a.元素名  select

      b.元素id  getRowCount

      c.参数类型 (不写)

      d.结果类型 int

      e.SQL定义 select count(*) from sys_roles where name like ?

代码实现:

<mapper namespace="com.jt.sys.dao.SysRoleDao">
<select id="findPageObjects"
resultType="com.jt.sys.entity.SysRole">
select *
from sys_roles
<include refid="queryWhereId"/>
order by createdTime desc
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount"
resultType="int">
select count(*)
from sys_roles
<include refid="queryWhereId"/>
</select>
<sql id="queryWhereId">
<where>
<if test="name!=null and name!=''">
name like concat("%",#{name},"%")
</if>
</where>
</sql>
</mapper>

7.Service层对象

业务描述:核心业务就是分页查询数据并对数据进行封装。

  1. 通过参数变量接收控制层数据

  2. 对数据进行合法验证

  3. 基于参数数据进行总记录数查询

  4. 基于参数数据进行当前页记录的查询

  5. 对数据进行封装

  6. ..........

业务实现:

1.Service接口定义

   a.包名:com.jt.sys.service

   b.接口名:SysRoleService

2.接口方法定义:

    a.方法名: findPageObjects

    b.参数列表:(String name,Integer pageCurrent)

     c.返回值:PageObject (通过此对象封装当前页数据,分页信息)

3.接口实现类定义

      a.包名: com.jt.sys.service.impl

      b.类名: SysRoleServiceImpl (实现SysRoleService接口)

 关键代码实现:

业务接口定义

public interface SysRoleService {
/**
* 本方法中要分页查询角色信息,并查询角色总记录数据
* @param pageCurrent 当表要查询的当前页的页码值
* @return 封装当前实体数据以及分页信息
*/
PageObject<SysRole> findPageObjects(
String name,Integer pageCurrent);
}

业务实现类定义

@Service
public class SysRoleServiceImpl implements SysRoleService{
@Autowired
private SysRoleDao sysRoleDao;
@Override
public PageObject<SysRole> findPageObjects(
String name, Integer pageCurrent) {
//1.验证参数合法性
//1.1验证pageCurrent的合法性,
//不合法抛出IllegalArgumentException异常
if(pageCurrent==null||pageCurrent<1)
throw new IllegalArgumentException("当前页码不正确");
//2.基于条件查询总记录数
//2.1) 执行查询
int rowCount=sysRoleDao.getRowCount(name);
//2.2) 验证查询结果,假如结果为0不再执行如下操作
if(rowCount==0)return null;
//3.基于条件查询当前页记录(pageSize定义为2)
//3.1)定义pageSize
int pageSize=2;
//3.2)计算startIndex
int startIndex=(pageCurrent-1)*pageSize;
//3.3)执行当前数据的查询操作
List<SysRole> records=
sysRoleDao.findPageObjects(name, startIndex, pageSize);
//4.对分页信息以及当前页记录进行封装
//4.1)构建PageObject对象
PageObject<SysRole> pageObject=new PageObject<>();
//4.2)封装数据
pageObject.setPageCurrent(pageCurrent);
pageObject.setPageSize(pageSize);
pageObject.setRowCount(rowCount);
pageObject.setRecords(records);
//5.返回封装结果。
return pageObject;
}

8.Controller 实现

业务描述:核心业务是处理客户端请求

  1. 接收客户端请求中的数据

  2. 基于请求调用业务方法进行请求处理

  3. 对处理结果进行封装(JsonResult)

  4. 将结果转换为json格式的字符串

  5. 将字符串通过服务器输出到客户端。

业务实现:

控制层对象SysRoleController类中方法定义

  1. 方法名   doFindPageObjets

  2. 参数列表 (String name,Integer pageCurrent)

  3. 返回值   JsonResult

  4. 映射     doFindPageObjets

关键代码实现:

@Controller
@RequestMapping("/role/")
public class SysRoleController {
@Autowired
private SysRoleService sysRoleService;
@RequestMapping("doRoleListUI")
public String doRoleListUI(){
return "sys/role_list";
}
@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects( String name,Integer pageCurrent){
PageObject<SysRole> pageObject=
sysRoleService.findPageObjects(name,pageCurrent);
return new JsonResult(pageObject);
} 
}

测试:部署测试

http://localhost:8080/CGB-JT-SYS-V1.01/role/doFindPageObjects.do?pageCurrent=1

效果:


5.客户端实现 

9.角色页面数据呈现

业务描述:

  1. 页面加载完成发起异步请求加载配置信息

  2. 通过服务端返回的数据更新当前列表页面

业务实现

  1. 定义doGetObjects()函数,通过此函数执行异步加载操作。

  2. 分页页面加载完成以后调用doGetObjects().

代码实现:

定义页面加载完成以后的事件处理

WEB-INF/pages/sys/role_list.html 

$(function(){
$("#pageId").load("doPageUI.do",function(){
//异步加载服务端数据然后进行呈现
doGetObjects();
});
})

定义分页查询函数

function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url="role/doFindPageObjects.do"
//? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)
//此数据会在何时进行绑定?(setPagination,doQueryObjects)
var pageCurrent=$("#pageId").data("pageCurrent");
//为什么要执行如下语句的判定,然后初始化pageCurrent的值为1
//pageCurrent参数在没有赋值的情况下,默认初始值应该为1.
if(!pageCurrent) pageCurrent=1;
var params={"pageCurrent":pageCurrent};
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//JsonResult->PageObject->List<SysConfigs>+...
//请问result是一个字符串还是json格式的js对象?对象
doHandleResponseResult(result);
}
);
}

设置异步响应结果

function doHandleResponseResult(result){
if(result.state==1){//ok
//更新table中tbody内部的数据
doSetTableBodyRows(result.data.records);//将数据呈现在页面上
//更新页面page.html分页数据
doSetPagination(result.data);
}else{
alert(result.message);
} 
}

将异步响应结果呈现在table的tbody位置

/*设置表格内容*/
function doSetTableBodyRows(records){
//1.获取tbody对象,并清空对象
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代records记录,并将其内容追加到tbody
for(var i in records){
//2.1 构建tr对象
var tr=$("<tr></tr>");
//2.2 构建tds对象
var tds=createdTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}
//构建body中每行的td元素


function createdTds(row){
var tds="<td>"+row.name+"</td>"+
"<td>"+row.note+"</td>"+
"<td>"+new Date(row.createdTime).toLocaleString()+"</td>"+
"<td>"+new Date(row.modifiedTime).toLocaleString()+"</td>"+
"<td>"+row.createdUser+"</td>"+
"<td>"+row.modifiedUser+"</td>"+
"<td>"+
"<button type='button' class='btn btn-delete'>delete</button>"+
"&nbsp"+
"<button type='button' class='btn btn-update'>update</button>"+
"</td>";
return tds;
}

效果:


 10.列表页面role_list.html信息查询实现

业务说明

  1. 列表查询按钮事件注册

  2. 列表查询按钮事件处理函数定义

  3. 列表查询参数获取以及传递

业务实现:

  1. 在$(function(){})回调函数中追加查询按钮的事件注册。

  2. 定义查询按钮的事件处理函数doQueryObjects

  3. 重用doGetObjects函数并添加查询参数name

关键代码实现:

查询按钮事件注册

$(".input-group-btn").on("click",".btn-search",doQueryObjects)

查询按钮事件处理函数定义

function doQueryObjects(){
//为什么要在此位置初始化pageCurrent的值为1?
//数据查询时页码的初始位置也应该是第一页
$("#pageId").data("pageCurrent",1);
//为什么要调用doGetObjects函数?
//重用js代码,简化jS代码编写。
doGetObjects();
}

在分页查询函数中追加name参数定义

function doGetObjects(){
//debugger;//断点调试
//1.定义url和参数
var url="role/doFindPageObjects.do"
//? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)
//此数据会在何时进行绑定?(setPagination,doQueryObjects)
var pageCurrent=$("#pageId").data("pageCurrent");
//为什么要执行如下语句的判定,然后初始化pageCurrent的值为1
//pageCurrent参数在没有赋值的情况下,默认初始值应该为1.
if(!pageCurrent) pageCurrent=1;
var params={"pageCurrent":pageCurrent};
//为什么此位置要获取查询参数的值?
//一种冗余的应用方法,目的时让此函数在查询时可以重用。
var name=$("#searchNameId").val();
//如下语句的含义是什么?动态在js对象中添加key/value,
if(name) params.name=name;//查询时需要
//2.发起异步请求
//请问如下ajax请求的回调函数参数名可以是任意吗?可以,必须符合标识符的规范
$.getJSON(url,params,function(result){
//请问result是一个字符串还是json格式的js对象?对象
doHandleResponseResult(result);
}
);
}

注意:

get请求乱码导致查询失败

数据库乱码

效果:


3.角色删除模块实现

6.服务端实现

11.DAO实现

业务描述:

  1. 接收业务层数据(id)

  2. 根据id删除角色自身信息

  3. 根据id删除角色与菜单的关系数据

  4. 根据id删除角色与用户的关系数据

业务实现

1.在SysRoleDao接口中添加删除对象的方法

   a.方法名 deleteObject;

   b.参数列表(Integer id)

   c.返回值int

2.在SysRoleMenuDao接口中添加删除对象的方法

    a.方法名 deleteObjectsByRoleId;

    b.参数列表(Integer roleId)

    c.返回值int

3.在SysUserRoleDao接口中添加删除对象的方法

     a.方法名 deleteObjectsByRoleId;

     b.参数列表(Integer roleId)

     c.返回值int

关键代码实现

SysRoleDao 接口中方法定义

int deleteObject(Integer id);

SysRoleMenuDao接口中方法定义

int deleteObjectsByRoleId(Integer roleId);

SysUserRoleDao接口方法定义

int deleteObjectsByRoleId(Integer roleId);

12.Mapper实现

业务描述:

  1. 基于SysRoleDao中删除方法,定义SQL元素

  2. 基于SysRoleMenuDao中删除方法,定义SQL元素

  3. 基于SysUserRoleDao中删除方法,定义SQL元素

业务实现:

1.在SysRoleMapper中添加对应的删除元素

   a.元素名 delete

   b.元素id deleteObject

   c.参数类型(int)

   d.SQL定义 (delete from sys_roles where id=#{id})

2.在SysRoleMenuMapper中添加对应的删除元素

    a.元素名 delete

    b.元素id deleteObjectsByRoleId

    c.参数类型(int)

     d.SQL定义 (delete from sys_role_menus where role_id=#{roleId})

3.在SysUserRoleMapper中添加对应的删除元素

     a.元素名 delete

      b.元素id deleteObjectsByRoleId

      c.参数类型(int)

      d.Sql定义(delete from sys_user_roles where id=#{id})

关键代码实现

SysRoleMapper文件中方法定义

<delete id="deleteObject"
parameterType="int">
delete
from sys_roles
where id=#{id}       
</delete>

SysRoleMenuMapper中方法定义

<delete id="deleteObjectsByRoleId"

parameterType="int">

delete

from sys_role_menus

where role_id=#{roleId}       

</delete>

SysUserRoleMapper 接口中方法定义

<delete id="deleteObjectsByRoleId"

parameterType="int">

delete

from sys_user_roles

where role_id=#{roleId}       

</delete>

13.Service实现

业务描述

  1. 接收控制层数据(id),并对数据进行合法性验证.

  2. 调用dao方法,执行删除操作

  3. 根据结果验证,反馈相关信息

业务实现(在SysRoleService接口中定义方法并在实现类中进行实现.)

  1. 方法名 deleteObject

  2. 参数列表 (Integer id)

  3. 返回值 int

代码实现:

@Override

public int deleteObject(Integer id) {

//1.验证参数的合法性

if(id==null||id<1)

throw new ServiceException("id的值不正确,id="+id);

//2.执行dao操作

int rows=sysRoleDao.deleteObject(id);

if(rows==0)

throw new ServiceException("数据可能已经不存在");

sysRoleMenuDao.deleteObjectsByRoleId(id);

sysUserRoleDao.deleteObjectsByRoleId(id);

//3.返回结果

return rows;

}

14.Controller实现

业务描述

  1. 接收客户端请求数据id

  2. 调用业务层方法删除数据

  3. 返回响应结果.

业务实现(在SysRoleController中定义删除方法)

  1. 方法名 doDeleteObject

  2. 参数列表 Integer id

  3. 返回值 JsonResult

代码实现:

@RequestMapping("doDeleteObject")
@ResponseBody
public JsonResult doDeleteObject(Integer id){
sysRoleService.deleteObject(id);
return new JsonResult("delete Ok");
}

7.客户端实现

15.角色列表页面删除操作实现

业务描述

点击每行的delete按钮时执行删除角色的操作

业务实现

  1. 页面加载完成以后在删除按钮上注册点击事件

$(function(){
$("#pageId").load("doPageUI.do",function(){
//异步加载服务端数据然后进行呈现
doGetObjects();
});
//查询按钮事件注册
$(".input-group-btn").on("click",".btn-search",doQueryObjects);
//删除按钮上注册点击事件
$("#tbodyId").on("click",".btn-delete",doDeleteObject);
});
/*
*将异步响应结果呈现在table的tbody位置
*/
function doSetTableBodyRows(records){
console.log("records:"+records);
//1.获取tbody对象,并清空对象
var tBody = $("#tbodyId");
tBody.empty();

//2.迭代records记录,并将其内容追加到tbody
for(var i in records){
//2.1 构建tr对象
var tr = $("<tr></tr>");
tr.data("id",records[i].id);

//2.2 构建tds对象
var tds = createdTds(records[i]);
//2.3 将tds追加到tr中
tr.append(tds);
//2.4 将tr追加到tbody中
tBody.append(tr);
}
}

1.事件处理函数doDeleteObject定义

关键代码实现:

function doDeleteObject(){
//1.获取选中的值(分页显示记录时在tr上要绑定id的值)
var id=$(this).parents("tr").data("id");
//2.构建参数对象
var params={“id”:id};
//3.异步请求执行删除
var url="role/doDeleteObject.do";
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doGetObjects();
}else{
alert(result.message);
}
})
}

4.角色添加页面呈现

8.服务端实现

16.角色Controller对象的实现

业务描述

基于客户端的请求返回一个页面(role_edit.html)

业务实现

  1. 方法名 doRoleEditUI

  2. 参数列表 ()

  3. 返回值 String

  4. 映射 @RequestMapping("doRoleEditUI")

 5.类名:SysRoleController.java

代码实现

@RequestMapping("doRoleEditUI")
public String doRoleEditUI(){
return "sys/role_edit";
}

9.客户端实现

17.角色列表添加按钮事件处理

业务描述

点击角色列表页面中的添加按钮时呈现角色添加页面.

业务实现:

  1. 添加按钮注册点击事件

  2. 定义事件处理函数(异步加载角色编辑页面)

代码实现:

文件名:role_list.html

$(".input-group-btn").on("click",".btn-add",doLoadEditUI);
//异步加载编辑页面
function doLoadEditUI(){
var title;
//hasClass函数用于判定对象中是否包含某个样式
if($(this).hasClass("btn-add")){
title="角色添加";
}else{
title="角色修改";
}
loadPageUI(url);
}
function loadPageUI(title){
$("#mainContentId")
.load("role/doRoleEditUI.do",function(){
$(".box-title").html(title);
});
}

效果:

18.角色添加页面菜单信息呈现role_edit.html

业务描述

页面加载完成,加载菜单信息,并通过菜单数据初始化菜单树(ZTree)

业务实现

定义doLoadSysMenus方法(负责加载ztree信息并初始化树结构)

关键代码实现

定义zTree配置

var zTree;
var setting = {
data : {
simpleData : {
enable : true,
idKey : "id",  //节点数据中保存唯一标识的属性名称
pIdKey : "parentId",  //节点数据中保存其父节点唯一标识的属性名称
rootPId : null  //根节点id
}
},
check:{
enable:true,
nocheckInherit:true
}
}

页面加载完成通过如下函数异步加载菜单信息

function doLoadSysMenus(){
var url="menu/doFindZtreeMenuNodes.do"
$.getJSON(url,function(result){
if(result.state==1){
zTree=$.fn.zTree.init(
$("#menuTree"),setting,result.data);
}else{
alert(result.message);
}
});
}

效果 :

5.角色添加操作实现

10.服务端实现

19.Dao实现

业务描述

  1. 将角色自身信息保存到数据库

  2. 将角色与菜单的关系数据保存到数据库

业务实现

1.在SysRoleDao接口中添加一个方法,用于将角色信息写入到数据库

   a.方法名 insertObject

   b.参数列表(SysRole entity)

   c.返回值 int

2.在SysRoleMenuDao中添加插入角色菜单关系数据的方法

   a.方法名 insertObject

   b.参数列表(Integer roleId, Integer[] menuIds)

   c.返回值 int

代码实现

SysRoleDao接口中方法定义

int insertObject(SysRole entity);

SysRoleMenuDao接口中方法定义(不存在则创建)

int insertObject(
@Param("roleId")Integer roleId,
@Param("menuIds")Integer[] menuIds);

20.Mapper实现

业务描述

  1. 基于SysRoleDao接口中insertObject方法定义相关元素

  2. 基于SysRoleMenuDao接口中insertObject方法定义相关元素

业务实现

1.在SysRoleMapper中定义插入数据的元素

   a.元素名 insert

  b.元素id insertObject

   c.参数类型 com.jt.sys.entity.SysRole

   d.SQL定义 (insert into sys_roles (....) values(....))

2.在SysRoleMenuMapper中定义插入数据的元素

   a.元素名 insert

   b.元素id insertObject

   c.参数类型 (无)

   d.SQL定义 (insert into sys_role_menus (....) values(....))

关键代码实现:

SysRoleMapper中元素定义

<insert id="insertObject"
parameterType="com.jt.sys.entity.SysRole"
useGeneratedKeys="true"
keyProperty="id">
insert into sys_roles
(id,name,note,createdTime,modifiedTime,
createdUser,modifiedUser)
values
(null,#{name},#{note},now(),now(),
#{createdUser},#{modifiedUser})
</insert>

SysRoleMenuMapper中元素定义

<insert id="insertObject">
insert into sys_role_menus
(role_id,menu_id)
values
<foreach collection="menuIds"
separator=","
item="item">
(#{roleId},#{item})
</foreach>
</insert>

21.Service实现

业务描述

  1. 通过参数变量接收控制层数据

  2. 对参数数据进行合法性验证.

  3. 保存角色自身信息数据到数据库

  4. 保存角色与菜单的关系数据到数据库

  5. 返回业务实现结果

业务实现

在SysRoleService接口中定义插入数据的方法

  1. 方法名 saveObject

  2. 参数列表 SysRole entity,Integer[] menuIds

  3. 返回值 int

关键代码实现

@Override
public int saveObject(SysRole entity,Integer[] menuIds) {
//1.合法性验证
if(entity==null)
throw new ServiceException("保存数据不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new ServiceException("角色名不能为空");
if(StringUtils.isEmpty(menuIds))
throw new ServiceException("必须为角色赋予权限");
//2.保存数据
int rows=sysRoleDao.insertObject(entity);
sysRoleMenuDao.insertObject(
entity.getId(),menuIds);
//3.返回结果
return rows;
}

22.Controller实现

业务描述

  1. 接收客户端请求数据并对数据进行封装

  2. 调用业务层方法将数据写入数据库

  3. 对控制层数据进行封装(JsonResult),并返回

业务实现

在SysRoleController中定义请求处理方法

  1. 方法名 doSaveObject

  2. 参数列表 (SysRole entity,Integer[] menuIds)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doSaveObject")

关键代码实现:

@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject(
SysRole entity,Integer[] menuIds){
sysRoleService.saveObject(entity,menuIds);
return new JsonResult("save ok");   
}

效果:


11.客户端实现

23.角色编辑页面按钮事件处理role_edit.html

业务描述

  1. 点击cancel按钮时,退出编辑页面进入列表页面.

  2. 点击save按钮时,将数据异步提交到服务端进行保存,保存ok时返回列表页面.

业务实现(cancel按钮事件处理)

function doCancel(){
$("#mainContentId")
.load("role/doRoleListUI.do",function(){
//移除绑定的数据(修改时会用)
$("#mainContentId").removeData();
});
}

业务实现(save按钮事件处理)

function doSaveOrUpdate(){
//1.获取表单数据
var params=getEditFormData();  
//2.异步提交表单数据
var insertUrl="role/doSaveObject.do";
$.post(insertUrl,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
});
}
//获取表单数据
function getEditFormData(){
var params={
name:$("#nameId").val(),
note:$("#noteId").val()
}
//获取选中的node节点
var menuIds=[];
var checkedNodes=
zTree.getCheckedNodes(true);
for(var i in checkedNodes){
menuIds.push(checkedNodes[i].id)
}
params.menuIds=menuIds.toString();//(1,2,3,4,5)
return params;
}

6.角色修改页面呈现

12.服务端实现

24.Dao实现

业务描述

  1. 基于角色id查询角色信息

  2. 基于角色id查询菜单信息

业务实现

1.在SysRoleDao中定义基于基于角色id查询角色信息的方法

     a.方法名 findObjectById

     b.参数列表 Integer id

     c.返回值 SysRole

2.在SysRoleMenuDao中定义基于角色查询菜单id的方法

     a.方法名 findMenuIdsByRoleId

     b.参数列表 Integer roleId

     c.返回值 List<Integer>

关键代码实现

SysRoleDao中方法定义

SysRole findObjectById(Integer id);

SysRoleMenuDao中方法定义

List<Integer> findMenuIdsByRoleId(Integer roleId);

25.Mapper实现

业务描述

  1. 基于SysRoleDao接口中方法的定义编写基于角色id执行查询角色的元素

  2. 基于SysRoleMenuDao接口中方法的定义编写基于角色id查询菜单id的方法

业务实现

1.SysRoleMapper中元素定义

   a.元素名  select

   b.元素id  findObjectById

   c.参数类型 (int)

   d.结果类型 com.jt.sys.entity.SysRole

   e.SQL定义 select * from sys_roles where id=?

2.SysRoleMenuMapper中元素定义

    a.元素名  select

    b.元素id  findMenuIdsByRoleId

    c.参数类型 (int)

    d.结果类型 int

    e.SQL定义 select menu_id from sys_role_menus where role_Id=?

关键代码实现

SysRoleMapper中元素定义

<select id="findObjectById"

resultType="com.jt.sys.entity.SysRole">

select *

from sys_roles

where id=#{id}

</select>

SysRoleMenuMapper中元素定义

<select id="findMenuIdsByRoleId"

resultType="int">

select menu_id

from sys_role_menus

where role_id=#{roleId}

</select>

26.Service实现

业务描述:基于角色id查询角色及关联的菜单信息

  1. 通过参数变量接收控制层数据

  2. 对数据进行合法验证

  3. 基于参数数据查询角色信息

  4. 基于参数数据查询菜单信息

  5. 对数据进行封装并返回

业务实现:

在SysRoleService接口及实现类中定义基于id查询的方法

关键代码实现

接口中方法定义

Map<String,Object> findObjectById(Integer id) ;

实现类中方法的实现

@Override
public Map<String,Object> findObjectById(Integer id) {
//1.合法性验证
if(id==null||id<=0)
throw new ServiceException("id的值不合法");
//2.执行查询
SysRole role=sysRoleDao.findObjectById(id);
List<Integer> menuIds=sysRoleMenuDao.findMenuIdsByRoleId(id);
//3.验证结果并返回
if(role==null)
throw new ServiceException("此记录已经不存在");
Map<String,Object> map=new HashMap<String, Object>();
map.put("role", role);
map.put("menuIds", menuIds);
return map;
}

27.Controller实现

业务描述:核心业务是处理客户端请求

  1. 接收客户端请求中的数据(id)

  2. 基于请求调用业务方法进行请求处理

  3. 对处理结果进行封装(JsonResult)

  4. 将结果转换为json格式的字符串

  5. 将字符串通过服务器输出到客户端。

业务实现:

控制层对象SysRoleController类中方法定义

  1. 方法名   doFindObjectById

  2. 参数列表 (Integer id)

  3. 返回值   JsonResult

  4. 映射     doFindObjectById

关键代码实现

@RequestMapping("doFindObjectById")

@ResponseBody

public JsonResult doFindObjectById(Integer id){

Map<String,Object> map=

sysRoleService.findObjectById(id);

return new JsonResult(map);

}

13.客户端实现

28.角色列表页面实现

业务描述

  1. 点击列表页面修改按钮时基于id查询异步角色信息

  2. 通过角色信息初始化编辑页面数据

业务实现

  1. 列表页面修改按钮事件注册

  2. 列表页面修改按钮事件处理函数定义(与添加操作公用一个方法并进行适当修改)

关键代码实现

$("tbody").on("click",".btn-update",doLoadEditUI);
/加载编辑页面
function doLoadEditUI(){
//定义页面标题(内容可能是添加角色也可能是修改角色)
var title;
//判定要执行的操作(是添加还是修改)
if($(this).hasClass("btn-add")){
title="添加角色";
loadPageUI(title);
}else{
title="修改角色";
//获取当前行的id值
var id=$(this).parents("tr").data("id");
//根据id查找记录,判定记录是否存在
var url="role/doFindObjectById.do";
var data={"id":id};
$.getJSON(url,data,function(result){
if(result.state==1){
//此位置除了要分析正确还要考虑对象不存在的情况
$("#mainContentId").data("data",result.data)
loadPageUI(title);
}else{
alert(result.message);
}
});
}
}  
function loadPageUI(title){
$("#mainContentId")
.load("role/doRoleEditUI.do",function(){
$(".box-title").html(title);
});
}

效果:


29.角色编辑页面实现

业务描述

1)菜单树初始化完成,获取绑定数据

2)通过绑定数据初始化表单

代码实现:

菜单树初始化完成,获取绑定数据,初始化表单

关键代码

function doLoadSysMenus(){
var url="menu/doFindZTreeNodes.do"
$.getJSON(url,function(result){
if(result.state==1){
ztree=$.fn.zTree.init(
//先有菜单树后再选择树数据
$("#menuTree"),setting,result.data);
var data=$("#mainContentId").data("data");
if(data){
doInitEditFormData(data);
}
}else{
alert(result.message);
}
})
}

初始化表单数据

function doInitEditFormData(data){
$("#nameId").val(data.role.name);
$("#noteId").val(data.role.note);
//展开所有节点
zTree.expandAll(true);
//勾选角色所拥有的菜单
var menuIds = data.menuIds;
for(var i=0; i<menuIds.length; i++) {
//获取key为id值为menuIds[i]的节点
var node = zTree.getNodeByParam("id",menuIds[i]);
//选中当前节点
zTree.checkNode(node,true,false);
}
}

效果:


 7.角色修改操作实现

14.服务端实现

30.Dao实现

业务描述

修改角色自身信息

修改角色与菜单的关系数据

业务实现:

1)在SysRoleDao方法中添加角色修改的方法

  1. 方法名 updateObject

  2. 参数列表 (SysRole entity)

  3. 返回值 int

2)在SysRoleMenuDao中添加根据角色id删除关系数据的方法

  1. 方法名 deleteObjectsByRoleId (假如已经存在则无需再次定义)

  2. 参数(Integer roleId)

  3. 返回值 int

关键代码实现:

SysRoleDao中方法定义

int updateObject(SysRole entity);

SysRoleMenuDao中方法定义

int deleteObjectsByRoleId(Integer roleId);

31.Mapper实现

业务描述

基于SysRoleDao中updateObject方法的定义编写对应sql元素

基于SysRoleMenuDao中deleteObjectsByRoleId方法的定义编写对应sql元素

业务实现

1)在SysRoleMapper文件中添加update元素,定义角色信息修改的sql语句

  1. 元素名 update

  2. 元素id updateObject

  3. 参数类型 com.jt.sys.entity.SysRole

  4. Sql定义(update sys_roles set.... where ....)

2)在SysRoleMenuMapper中添加删除数据的元素

  1. 元素名  delete

  2. 元素id  deleteObjectsByRoleId

  3. 参数类型  int

  4. Sql 定义 delete from sys_role_menus where role_id=#{roleId};

代码实现:

SysRoleMapper元素定义

<update id="updateObject" parameterType="com.jt.sys.entity.SysRole">
update sys_roles
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="note!=null and note!=''">
note=#{note},
</if>
<if test="modifiedUser!=null and modifiedUser!=''">
modifiedUser=#{modifiedUser},
</if>
modifiedTime=now()
</set>
where id=#{id}
</update>

SysRoleMenuMapper 元素定义(假如已有则不用再写)

<delete id="deleteObjectsByRoleId"

parameterType="int">

delete from sys_role_menus

where role_id=#{roleId}

</delete>

32.Service实现

业务描述

  1. 接收控制层数据,对数据进行合法验证.

  2. 调用dao层方法更新数据

  3. 验证结果,并返回.

业务实现(在SysRoleService接口及实现类中定义方法及实现.)

  1. 方法名 updateObject

  2. 参数列表 SysRole entity,String menuIds

  3. 返回值 int

代码实现:

@Override
public int updateObject(SysRole entity,String menuIds) {
//1.合法性验证
if(entity==null)
throw new ServiceException("更新的对象不能为空");
if(entity.getId()==null)
throw new ServiceException("id的值不能为空");
if(StringUtils.isEmpty(entity.getName()))
throw new ServiceException("角色名不能为空");
if(StringUtils.isEmpty(menuIds))
throw new ServiceException("必须为角色指定一个权限");
//2.更新数据
int rows=sysRoleDao.updateObject(entity);
if(rows==0)
throw new ServiceException("对象可能已经不存在");
sysRoleMenuDao.deleteObjectsByRoleId(entity.getId());
sysRoleMenuDao.insertObject(entity.getId(),menuIds.split(","));
//3.返回结果
return rows;
}

33.Controller实现

业务描述

  1. 接收客户端请求数据(角色信息,以及菜单id)

  2. 调用业务层对象处理业务

  3. 对结果进行封装

  4. 将响应结果转换为json输出

业务实现(在SysRoleController中定义修改方法)

  1. 方法名 doUpdateObject

  2. 参数列表(SysRole entity)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doUpdateObject")

代码实现:

@RequestMapping("doUpdateObject")
@ResponseBody
public JsonResult doUpdateObject(SysRole entity,
String menuIds){
sysRoleService.updateObject(entity,menuIds);
return new JsonResult("update ok");

15.客户端实现

34.角色编辑页面数据的更新

业务描述:用户点击保存按钮时异步提交表单数据

  1. 获取表单数据(相对于添加操作应该再添加一个id)

  2. 异步提交(post)表单数据(修改和添加不是同一个url)

  3. 关闭页面,显示列表页面

业务实现:

  1. 修改doSaveOrUpdate方法与添加操作共用此方法

  2. 与添加操作共用一个获取表单的方法

关键代码实现

修改doSaveOrUpdate函数(与保存操作共享一个函数)

//点击保存按钮时执行此方法
function doSaveOrUpdate(){//insert/update
//获取表单数据
var params=getEditFormData();
//假如当前页面.container-fluid对象上绑定着值说明是修改
var data=$("#mainContentId").data("data");
if(data){
params.id=data.role.id;//修改时表单数据中需要添加id
}
//根据当前页面上是否绑定着值来定义url
var insertUrl="role/doSaveObject.do";
var updateUrl="role/doUpdateObject.do";
var url=data?updateUrl:insertUrl;
//异步提交数据
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
})
}

获取表单数据

function getEditFormData(){
var params={
name:$("#nameId").val(),
note:$("#noteId").val()
}
//获取选中的node节点
var menuIds=[];
var checkedNodes=
ztree.getCheckedNodes(true);
for(var i in checkedNodes){
menuIds.push(checkedNodes[i].id)
}
params.menuIds=menuIds.toString();
return params;
}

效果:

 去掉部分数据选项

再次点击更新:

成功:

代码中的bug:

解决方案:

没有数据时也修改一些页码页数据

课后作业: 

做完组织管理


 8.总结

16.重点和难点分析

1.角色业务处理流程(持久层,业务层,控制层,表示层)

2.角色业务数据封装(SysRole,PageObject,JsonResult)

3.角色业务异常的统一处理(统一异常处理类的定义)

4.角色日期数据的格式转换(3种方案)

5.角色页面中JS实现(业务,方法的应用,编写,调试)

6.角色查询业务实现流程(持久层,mapper,业务层,控制层,表示层)

7.角色删除业务的具体实现流程(..........)

8.角色列表页面如何获取选中的checkbox对应的id值

9.角色列表页面如何实现全选和取消全选操作.(扩展实现)

思考JS函数:(doc.tedu.cn)

  1. ajax({url:url,type:post,dataType:json,data:params,success:function(){}})

  2. load(url,[params],[callback])

  3. getJSON(url,params,function(result){})

  4. data(key,[value])

  5. prop(propertyName,[propertyValue])

  6. on("click",".pre,.next",doJumpToPage)

  7. post(url,[params],[callback])

  8. change()

  9. each(function(){})

  10. ..............


17.常见FAQ

  1. 为什么要定义实体类?(实现和表之间的映射,更好的封装数据)

  2. 类为什么要实现序列化接口?(已知)

  3. 类实现序列化接口以后为什么要添加序列化版本id?(已知道)

  4. 如何实现对象的序列化时的粒度控制?(哪些属性要序列化,哪些属性不需要序列化)(已知)

  5. 类对象序列化时内容如何加密?(已知)

  6. 角色业务层查询到的数据是如何封装的?(PageObject)

  7. 角色控制层数据是如何封装的?(JsonResult)

  8. 角色控制层异常是如何处理的?(全局统一处理)

  9. 角色控制层方法返回的对象是如何转换为JSON串的?(jackson)

  10. 角色控制层日期类型的数据是如何处理的?

  11. 实体对象SysRole的set/get方法何时有调用?

  12. 映射文件中的sql元素用于做什么?(提取sql共性)

  13. 映射文件中的动态sql元素常用的有哪些?(foreach,where,if,set,...)


 1

                                                                        齐雷


day05:京淘项目-权限管理子系统-用户模块

1.    用户模块列表呈现                                        3

1.1.    服务端呈现                                        3

1.1.1.    Controller实现                              3

1.2.    客户端呈现                                        4

1.2.2.    首页Starter页面异步加载                      4

1.2.3.    用户列表页面加载分页页面                      4

2.    用户模块列表数据呈现                                     4

2.3.    服务端实现                                        4

2.3.4.    Entity类实现                                4

2.3.5.    Dao接口实现                                 9

2.3.6.    Mapper文件实现                             10

2.3.7.    Service接口及实现类                         12

2.3.8.    Controller类实现                           13

2.4.    客户端实现                                       14

2.4.9.    用户列表页面异步请求加载数据                  14

2.4.10.    用户列表页面数据异步查询实现                 16

3.    用户禁用启用操作实现                                    17

3.5.    服务端实现                                       17

3.5.11.    Dao接口实现                               17

3.5.12.    Mapper文件实现                            17

3.5.13.    Service接口实现                           18

3.5.14.    Controller类实现                          19

3.6.    客户端实现                                       19

3.6.15.    用户列表页面禁用按钮事件处理                 20

4.    用户添加页面呈现                                       21

4.7.    服务端实现                                       21

4.7.16.    用户Controller实现                        21

4.8.    客户端实现                                       21

4.8.17.    用户列表页面添加按钮事件处理                 21

5.    用户添加页面菜单数据呈现                                22

5.9.    服务端实现                                       22

5.9.18.    Vo对象定义                                22

5.9.19.    Dao接口实现                               23

5.9.20.    Mapper文件实现                            24

5.9.21.    Service接口实现                           24

5.9.22.    Controller实现                            25

5.10.    客户端实现                                      25

5.10.23.    用户编辑页面实现                          25

6.    用户数据保存实现                                       26

6.11.    服务端实现                                      26

6.11.24.    Dao 接口实现                             26

6.11.25.    Mapper文件实现                           27

6.11.26.    用户Service实现                          28

6.11.27.    用户Controller实现                       29

6.12.    客户端实现                                      30

6.12.28.    用户编辑页面事件处理                       30

7.    用户页面修改呈现                                       32

7.13.    服务端实现                                      32

7.13.29.    用户DAO实现                              32

7.13.30.    用户Mapper实现                           32

7.13.31.    用户角色DAO实现                           33

7.13.32.    用户角色Mapper实现                        33

7.13.33.    用户Service实现                          34

7.13.34.    用户Controller实现                       35

7.14.    客户端实现                                      35

7.14.35.    用户列表页面业务实现                       35

7.14.36.    用户编辑页面数据初始化                     37

8.    用户编辑页面数据更新                                    38

8.15.    服务端实现                                      38

8.15.37.    用户Dao实现                              38

8.15.38.    用户Mapper实现                           39

8.15.39.    用户角色Dao实现                           40

8.15.40.    用户角色Mapper实现                        40

8.15.41.    用户Service实现                          41

8.15.42.    用户Controller实现                       42

8.16.    客户端实现                                      42

8.16.43.    用户编辑页面事件处理.                      43

9.    总结                                                  44

9.17.    重点和难点分析                                  44

9.18.    常见FAQ                                        44


  1. 用户模块列表呈现(到这里了) 

    1. 服务端呈现

  1. Controller实现

业务描述

创建一个Controller并定义一个方法,此方法直接返回用户列表页面.

类的定义

  1. 包名:com.jt.sys.controller

  2. 类名:SysUserController

  3. 映射:@RequestMapping("/user/")

类中方法定义

  1. 方法名 listUI

  2. 参数列表()

  3. 返回值 String

  4. 映射 @RequestMapping("doUserListUI")

代码实现:

@Controller
@RequestMapping("/user/")
public class SysUserController {
@RequestMapping("doUserListUI")
public String doUserListUI(){
return "sys/user_list";
}
}

2.客户端呈现

2.首页Starter页面异步加载

业务描述

点击用户管理时,启动异步加载操作,将服务端返回页面,

添加到starter页面的div中.

代码实现:

<script type="text/javascript">
$(function(){
…
doLoadUI("load-user-id","user/doUserLsistUI.do");
});
</script>

3.用户列表页面加载分页页面

业务描述

用户列表页面加载完成,异步加载分页页面.

代码实现

$(document).ready(function(){
$("#pageId").load("doPageUI.do");
});

2.用户模块列表数据呈现

3.服务端实现

4.Entity类实现

业务描述

  1. 定义实体对象实现与sys_users表的映射

  2. 定义值对象封装查询时获取的用户与部门相关信息

业务实现

  1. 构建实体对象,属性与表中字段有映射关系

  2. 提供set/get,无参构造方法,重写toString方法

  3. 实现序列化接口,添加序列化版本id

1.实体对象类的定义

    a.包名 com.jt.sys.entity

    b.类名 SysUser (实现序列化接口并添加序列化ID)

    c.属性 与sys_users表中字段对应

    d.方法 提供set/get方法

代码实现:

实体类定义(后续添加和修改时可以用于在控制层接收客户端数据)

//定义值对象封装查询时获取的用户与部门相关信息public class SysUser implements Serializable{
private static final long serialVersionUID = 177030063138338860L;
private Integer id;
private String username;
private String password;
private String salt;
private String email;
private String mobile;
private Integer valid=1;
private Integer deptId;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Integer getValid() {
return valid;
}
public void setValid(Integer valid) {
this.valid = valid;
}
@JsonSerialize(using=DateJsonSerializer.class)
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
@JsonSerialize(using=DateJsonSerializer.class)
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
}

值对象定义(封装用户信息与其关联的部门信息的查询结果)

public class SysUserDeptResult implements Serializable{
private static final long serialVersionUID = 5477389876913514595L;
private Integer id;
private String username;
private String password;//md5
private String salt;
private String email;
private String mobile;
private Byte valid=1;
private SysDept sysDept;
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
public Byte getValid() {
return valid;
}
public void setValid(Byte valid) {
this.valid = valid;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public SysDept getSysDept() {
return sysDept;
}
public void setSysDept(SysDept sysDept) {
this.sysDept = sysDept;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
}

5.Dao接口实现

业务描述

创建用户持久层接口并定义查询方法获取表中用户信息.

接口定义

  1. 包: com.jt.sys.dao

  2. 接口名:SysUserDao

接口中方法定义(依据查询当前页数据)

  1. 方法名 findPageObjects

  2. 参数列表(String username,Integer startIndex,Integer pageSize)

  3. 返回值List<SysUser>

接口中方法定义(依据条件查询总记录数)

  1. 方法名 getRowCount

  2. 参数列表(String username)

  3. 返回值int

代码实现:

public interface SysUserDao {
List<SysUserDeptResult> findPageObjects(
@Param("username") String username,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize);
int getRowCount(@Param("username") String username);
}

6.Mapper文件实现

业务描述

1)依据SysUserDao接口及方法定义SysUserMapper文件和mapper元素。

业务实现:

1.创建映射文件

   a.包名:mapper.sys

   b.文件名:SysUserMapper.xml

   c.命名空间 com.jt.sys.dao.SysUserDao

2.在SysDeptMapper文件中定义基于id查询部门信息的元素

   a.元素名  select

   b.元素id  findDeptById

   c.参数类型 (不写)

   d.结果映射 com.jt.sys.entity.SysDept

   e.SQL定义 select * from sys_depts where id=?

3.在SysUserMapper文件中创建映射元素实现分页查询操作

    a.元素名  select

    b.元素id  findPageObjects

    c.参数类型 (不写)

    d.结果映射 sysUserMap

    e.SQL定义 select * from sys_users where username like ? limit …

4.在SysUserMapper文件中创建映射元素实现查询统计操作

    a.元素名  select

     b.元素id  getRowCount

     c.参数类型 (不写)

     d.结果类型 int

     e.SQL定义 select count(*) from sys_users where username like ?

代码实现

SysDeptMapper文件中元素定义

<select id="findById"

resultType="com.jt.sys.entity.SysDept">

select *

from sys_depts

where id=#{id}

</select>

SysUserMapper文件中元素定义

<?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.jt.sys.dao.SysUserDao">
<resultMap id="sysUserMap"
type="com.jt.common.vo.SysUserDeptResult">
<!-- 关联查询 -->
<association property="sysDept"
column="deptId"
select="com.jt.sys.dao.SysDeptDao.findById">
</association>
</resultMap>
<select id="findPageObjects"
resultMap="sysUserMap">
select * from sys_users
<include refid="queryWhereId"/>
limit #{startIndex},#{pageSize}
</select>
<select id="getRowCount"
resultType="int">
select count(*) from sys_users
<include refid="queryWhereId"/>
</select>
<sql id="queryWhereId">
<where>
<if test="username!=null and username!=''">
username like concat("%",#{username},"%")
</if>
</where>
</sql>
</mapper>

7.Service接口及实现类

业务描述

  1. 接收控制层数据,并对数据进行合法验证.

  2. 调用dao层方法,依据条件获取当前数据以及总记录数

  3. 封装数据并返回.

业务实现

业务层接口定义

  1. 包名 com.jt.sys.service

  2. 接口名 SysUserService

接口方法定义

  1. 方法名 findPageObjects

  2. 参数列表(String username,Integer pageCurrent)

  3. 返回值 PageObject

接口实现类定义

  1. 包名 com.jt.sys.service.impl

  2. 类名 SysUserServiceImpl

代码实现

接口定义

public interface SysUserService {
PageObject<SysUserDeptResult> findPageObjects(
String username,
Integer pageCurrent);
}

接口实现类及方法定义

@Service
public class SysUserServiceImpl implements SysUserService {
@Resource
private SysUserDao sysUserDao;
@Autowired
private SysUserRoleDao sysUserRoleDao;
@Override
public PageObject<SysUserDeptResult> findPageObjects(String username,
Integer pageCurrent) {
//1.数据合法性验证
if(pageCurrent==null||pageCurrent<=0)
throw new ServiceException("参数不合法");
//2.计算startIndex的值
int pageSize=3;
int startIndex=(pageCurrent-1)*pageSize;
//3.依据条件获取当前页数据
List<SysUserDeptResult> records=
sysUserDao.findPageObjects(
username, startIndex, pageSize);
//4.依据条件获取总记录数
int rowCount=sysUserDao.getRowCount(username);
//5.封装数据
PageObject<SysUserDeptResult> pageObject=new PageObject<>();
pageObject.setPageCurrent(pageCurrent);
pageObject.setRowCount(rowCount);
pageObject.setPageSize(pageSize);
pageObject.setRecords(records);
return pageObject;
}
}

8.Controller类实现

业务描述

  1. 接收客户端请求数据

  2. 调用业务方法处理请求

  3. 封装响应数据并返回

业务实现(控制层SysUserController类中方法定义)

  1. 方法名 doFindPageObjects

  2. 参数列表(String username,Integer pageCurrent)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doFindPageObjects")

代码实现:

在SysUserController类添加方法。

@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects(
String username,Integer pageCurrent){
PageObject<SysUser> pageObject=
sysUserService.findPageObjects(
username, pageCurrent);
return new JsonResult(pageObject);
}

效果:


4.客户端实现

9.用户列表页面异步请求加载数据

业务描述

  1. 分页div加载完成,异步加载数据.

  2. 将数据更新到页面.

业务实现(异步请求方法定义)

  1. 方法名 doGetObjects

     2.参数列表()

$(document).ready(function(){

$("#pageId").load("doPageUI.do",doGetObjects);

});
function doGetObjects(){
//url
var url="user/doFindPageObjects.do";
//获取参数数据
var pageCurrent=$("#pageId")
.data("pageCurrent");
if(!pageCurrent)pageCurrent=1;
var  params={"pageCurrent":pageCurrent}
//获取查询参数username的值(此步骤在查询时使用)
var username=$("#searchNameId").val();
if(username)params.username=username;
//发送异步请求
$.getJSON(url,params,function(result){
if(result.state==1){
setTableBodyRows(result.data.records);
setPagination(result.data);
}else{
alert(result.message);
}
});
}
function setTableBodyRows(records){
//1.获取body对象,并清空内容
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代records元素,将内容添加到body
for(var i in records){
//2.1.创建tr对象
var tr=$("<tr></tr>");
tr.data("user",records[i]);
//2.2创建td元素
var tds=createTds(records[i]);
//2.3将tr追加到tr中
tr.append(tds);
//2.4将tr追加到tbody中
tBody.append(tr);
}
}
function createTds(data){
console.log("data.id="+data.id);
var tds=
"<td><input type='radio' name='checkItem' value='"+data.id+"'/></td>"+
"<td>"+data.username+"</td>"+
"<td>"+data.sysDept.name+"</td>"+
"<td>"+data.email+"</td>"+
"<td>"+data.mobile+"</td>"+
"<td>"+(data.valid?'启用':'禁用')+"</td>"+
"<td>"+data.createdTime+"</td>"+
"<td>"+data.modifiedTime+"</td>"+
"<td><button type='button' class='btn btn-default btn-valid'>"+(data.valid?'禁用':'启用')+"</button></td>";
return tds;
}

10.用户列表页面数据异步查询实现

业务描述

  1. 查询按钮事件注册

  2. 查询按钮事件处理函数定义(执行查询操作,重用doGetObjects函数)

        a.初始化当前页码

        b.获取表单参数

        c.异步提交请求.

代码实现:

$(document).ready(function(){
$("#pageId").load("doPageUI.do",doGetObjects);
$(".input-group-btn")
.on("click",".btn-search",doQueryObjects)
});
function doQueryObjects(){
//1.初始化分页pageCurrent的值
$("#pageId").data("pageCurrent",1);
//2.异步查询
doGetObjects();
}

效果:


3.用户禁用启用操作实现

5.服务端实现

11.Dao接口实现

业务描述

定义一个方法,根据业务层传入的数据,对用户信息执行禁用或启用操作.

业务实现(在SysUserDao中定义禁用启用方法)

  1. 方法名 validById

  2. 参数列表(Integer id,Integer valid,String modifiedUser)

  3. 返回值 int

代码实现:

int validById(

@Param("id")Integer id,

@Param("valid")Integer valid,

@Param("modifiedUser")String modifiedUser);

12.Mapper文件实现

业务描述

在SysUserMapper中定义与SysUserDao中方法对应的禁用启用元素

元素定义

  1. 元素名 update

  2. 元素id validById

  3. 元素参数(不写)

  4. SQL (update sys_users set ... where ....)

代码实现:

<update id="validById">
update sys_users
set valid=#{valid},
modifiedUser=#{modifiedUser},
modifiedTime=now()
where id=#{id}
</update>

13.Service接口实现

业务描述

  1. 接收控制层数据(id,valid,modifiedUser)

  2. 对数据进行合法性验证

  3. 调用dao层方法执行更新操作(禁用或启用)

  4. 对结果进行验证,并返回.

业务实现(在SysUserService接口及实现类定义禁用启用方法)

  1. 方法名 validById

  2. 方法参数列表(Integer id,Integer valid,String modifiedUser)

  3. 返回值int

代码实现

接口实现类中方法的实现。

@Override
public int validById(Integer id,Integer valid,String modifiedUser) {
//1.合法性验证
if(id==null||id<=0)
throw new ServiceException("参数不合法,id="+id);
if(valid!=1&&valid!=0)
throw new ServiceException("参数不合法,valie="+valid);
if(StringUtils.isEmpty(modifiedUser))
throw new ServiceException("修改用户不能为空");
//2.执行禁用或启用操作
int rows=0;
try{
rows=sysUserDao.validById(id, valid, modifiedUser);
}catch(Throwable e){
e.printStackTrace();
//报警,给维护人员发短信
throw new ServiceException("底层正在维护");
}
//3.判定结果,并返回
if(rows==0)
throw new ServiceException("此记录可能已经不存在");
return rows;
}

14.Controller类实现

业务描述

  1. 接收客户端请求数据(id,valid)

  2. 调用业务层对应方法执行禁用启用操作

  3. 封装响应数据,并返回

业务实现(SysUserController类中定义禁用启用方法)

  1. 方法名 doValidById

  2. 方法参数 (Integer id,Integer valid)

  3. 方法返回值 JsonResult

  4. 方法映射@RequestMapping("doValidById")

代码实现

@RequestMapping("doValidById")
@ResponseBody
public JsonResult doValidById(
Integer id,
Integer valid){
sysUserService.validById( id, valid,"admin");//"admin"用户将来是登陆用户
return new JsonResult("update ok");
}

6.客户端实现

15.用户列表页面禁用按钮事件处理

业务描述

  1. 按钮事件注册(btn-valid)

  2. 按钮事件处理函数定义(doValidById)

代码实现

$(document).ready(function(){
…
$("#tbodyId")
.on("click",".btn-valid",doValidById)
});
function doValidById(){
var url="user/doValidById.do";
var rowData=$(this).parents("tr").data("rowData");
var valid=$(this).parents("tr").data("valid");
var params={"id":rowData.id,"valid":rowData.valid?0:1};
var btn=$(this);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doGetObjects();//重新查询刷新
//优化方式实现
doEditRow(btn,valid);
}else{
alert(result.message);
}
})   
}   

局部刷新(扩展实现)

function doEditRow(obj,valid){
console.log(obj);
//1.修改按钮上的内容
$(obj).html(valid?"启用":"禁用");
//2.获取当前行tr对象,然后重新绑定新的值
var tr=$(obj).parents("tr");
tr.data("valid",valid?0:1);
//3.修改tr中第5个td中的内容(查询API文档)
tr.find("td:eq(5)").text(valid?"禁用":"启用");
}

效果:


4.用户添加页面呈现

7.服务端实现

16.用户Controller实现

业务描述

在SysUserController对象中添加一个方法,返回编辑页面

业务实现(方法定义)

  1. 方法名 editUI

  2. 返回值 String

  3. 参数列表()

  4. 方法映射 @RequestMapping("doUserEditUI")

代码实现:

@RequestMapping("doUserEditUI")
public String doUserEditUI(){
return "sys/user_edit";
}

8.客户端实现

17.用户列表页面添加按钮事件处理

业务描述

  1. 添加按钮事件注册

  2. 添加按钮事件处理(核心是异步加载页面)

代码实现

$(document).ready(function(){
$(".input-group-btn")
.on("click",".btn-add ",doLoadEditUI)
});

定义页面加载方法,后续修改时也调用此方法,然后进行修改完善。

function doLoadEditUI(){
//1.判定点击的对象
var title;
if($(this).hasClass("btn-add")){
title="添加用户";
doLoadPage(title);
}else if($(this).hasClass("btn-update")){
title="修改用户";
var id=doGetCheckedId();
console.log("id="+id)
if(!id){
alert("请先选择");
return;
}
//基于id进行查询并加载编辑页面
doFindObjectById(id,title);
}
}
function doFindObjectById(id,title){
//1.params
var params={"id":id};
//2.url
var url="user/doFindObjectById.do";
//3.ajax request
$.getJSON(url,params,function(result){//JsonResult
if(result.state==1){
$("#mainContentId").data("rowData",result.data);
doLoadPage(title);  
}else{
alert(result.message);  
}
});
}
function doLoadPage(title){
var url="user/doUserEditUI.do"
$("#mainContentId").load(url,function(){
$(".box-title").html(title);
})
}

5.用户添加页面菜单数据呈现

核心业务

页面加载完成以后,启动异步请求从服务端获取角色信息初始化到页面上。

9.服务端实现

18.Vo对象定义

业务描述

借助此对象封装角色id,角色名称.

业务实现(定义类并实现序列化接口,添加版本id,提供set/get)

  1. 包名 com.jt.common.vo

  2. 类名 CheckBox

  3. 字段 (Integer id,String name)

代码实现:

public class CheckBox implements Serializable{
private static final long serialVersionUID = 2031967811425337153L;
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "CheckBox [id=" + id + ", name=" + name + "]";
}
}

19.Dao接口实现

业务描述

在SysRoleDao接口中添加一个查询角色ID,角色名的方法

业务实现(方法定义)

  1. 方法名 findObjects

  2. 方法参数()

  3. 方法返回值 List<CheckBox>

代码实现:

 List<CheckBox> findObjects();

20.Mapper文件实现

业务描述

在SysRoleMapper文件中添加查询角色id和名字的元素

业务实现

  1. 元素名 select

  2. 元素id findObjects

  3. 结果类型 com.jt.common.vo.CheckBox

  4. SQL (select id,name from sys_roles)

代码实现

<select id="findObjects"
resultType="com.jt.common.vo.CheckBox">
select id,name
from sys_roles
</select>

21.Service接口实现

业务描述

通过SysRoleDao中的方法获取所有的角色id,角色名

业务实现(在SysRoleService接口及实现中定义方法)

  1. 方法名 findObjects

  2. 参数列表 ()

  3. 返回值 List<CheckBox>

代码实现:

@Override

public List<CheckBox> findObjects() {

return sysRoleDao.findObjects();

}

22.Controller实现

业务描述

1)依据请求获取角色id和角色名

2)封装数据,并返回.

业务实现(方法定义)

  1. 方法名 doFindObjects

  2. 参数列表()

  3. 返回值 JsonResult

  4. 方法映射 @RequestMapping("doFindObjects")

代码实现:

@RequestMapping("doFindObjects")

@ResponseBody

public JsonResult doFindObjects(){

return new JsonResult(

sysRoleService.findObjects(),"query ok");

}

10.客户端实现

23.用户编辑页面实现

业务描述

页面加载完成,启动异步任务加载角色信息,初始化页面角色内容.

代码实现

$(document).ready(function(){
doLoadRoles();
//事件注册
$(".box-footer")
.on("click",".btn-cancel",doCancel)
.on("click",".btn-save",doSaveOrUpdate);
$("#treeLayer")
.on("click",".btn-cancel",doHideTree)
.on("click",".btn-confirm",doConfirm);
$(".form-horizontal")
.on("click",".load-sys-dept",doLoadZTreeNodes);
}
//页面加载完成,加载角色信息
function doLoadRoles(){
var url="role/doFindObjects.do"
$.getJSON(url,function(result){
if(result.state==1){
doInitPageRoles(result.data);
doInitFormData();//修改时
}else{
alert(result.message);
}
})
}
//初始化表单角色数据
function doInitPageRoles(data){
//1.获取角色要显示的位置对象
var div=$("#rolesId");
div.empty();
//2.迭代数据,将数据追加到div
var input="<input type='checkbox' name='roleId' value='[id]'>[name]"
for(var i in data){
//记住每次replace时都会产生一个新的字符串对象
var newInput=
input.replace("[id]",data[i].id)
.replace("[name]",data[i].name);
div.append(newInput)
}
}
function doInitFormData(){
var data=$("#mainContentId").data("rowData");
if(!data)return;
console.log(data);
//初始化用户信息
$("#usernameId").val(data.user.username);
$("#deptId").val(data.user.sysDept.name);
console.log("data.user.sysDept.idss="+data.user.sysDept.id);
$("#deptId").data("deptId",data.user.sysDept.id);
$("#emailId").val(data.user.email);
$("#phoneId").val(data.user.mobile);
//初始化用户角色信息
var ids=data.roleIds;
for(var i in ids){
$("#rolesId input[value='"+ids[i]+"']")
.prop("checked",true);
}
}
function doHideTree(){
$("#treeLayer").css("display","none");
}
//确定按钮
function doConfirm(){
//1.获取选中的记录(id,name);
var selectedNodes=zTree.getSelectedNodes();
var node=selectedNodes[0];
//2.将id和name填写或绑定在具体对象上
$("#deptId").val(node.name);
console.log("node.id="+node.id)
$("#deptId").data("deptId",node.id)
//3.隐藏zTree对应的Div
doHideTree();
}
function doLoadZTreeNodes(){
var url="dept/doFindZTreeNodes.do";
$("#treeLayer").css("display","block");
$.getJSON(url,function(result){
if(result.state==1){
zTree = $.fn.zTree.init($("#zTreeId"),setting,result.data);
}else{
alert(result.message);
}
});
}

效果:

点击上级部门初始化部门数据:


6.用户数据保存实现

11.服务端实现

24.Dao 接口实现

业务描述

  1. 接收业务层数据(SysUser),并将实体对象数据保存到数据库

  2. 接收用户和角色的关系数据,并将数据写入到中间关系表中.

业务实现

1)在SysUserDao中添加插入方法

  1. 方法名 insertObject

  2. 参数列表(SysUser entity)

  3. 返回值 int

2)在SysUserRoleDao接口中定义方法

  1. 方法名 insertObject

  2. 参数列表(Integer userId,Integer[] roleIds)

  3. 返回值 int

代码实现

/**
* 负责将用户信息写入到数据库
* @param entity
* @return
*/
int insertObject(SysUser entity);
/**
* 负责将用户与角色的关系数据写入到数据库
* @param userId 用户id
* @param roleIds 多个角色id
* @return
*/
int insertObject(
@Param("userId")Integer userId,
@Param("roleIds")Integer[] roleIds);

25.Mapper文件实现

业务描述

  1. 基于SysUserDao中insertObject方法定义SQL映射元素

  2. 基于SysUserRoleDao中insertObject方法定义SQL映射元素

业务实现:

1.在SysUserMapper中定义insert元素,实现向对应表中写入数据

   a.元素名 insert

   b.元素id insertObject

   c.参数类型 com.jt.sys.entity.SysUser

   d.SQL (insert into sys_users (…) values (…));

2.在SysUserRoleMapper中定义insert元素,实现向对应表中写入数据

   a.元素名 insert

   b.元素id insertObject

   c.SQL (insert into sys_user_roles (…) values (…));

代码实现

SysUserMapper中元素定义

<insert id="insertObject"
parameterType="sysUser"
useGeneratedKeys="true"
keyProperty="id">
insert into sys_users
(username,password,email,mobile,salt,valid,
createdTime,modifiedTime,createdUser,modifiedUser)
values
(#{username},#{password},#{email},#{mobile},#{salt},#{valid},
now(),now(),#{createdUser},#{modifiedUser})
</insert>

SysUserRoleMapper中元素定义

<insert id="insertObject">
insert into sys_user_roles
(user_id,role_id)
values
<foreach collection="roleIds" separator="," item="item">
(#{userId},#{item})
</foreach>
</insert>

26.用户Service实现

业务描述

  1. 接收客户端数据,并进行合法验证

  2. 将数据保存到数据库(用户信息,用户角色关系信息)

  3. 验证结果,并返回.

业务实现

在SysUserService接口及实现类定义方法

  1. 方法名 saveObject

  2. 参数列表(SysUser entity,Integer[] roleIds)

  3. 返回值int

代码实现

@Override
public int saveObject(SysUser entity, Integer[] roleIds) {
//1.验证数据合法性
if(entity==null||roleIds==null)
throw new ServiceException("数据参数不能为空");
if(StringUtils.isEmpty(entity.getUsername()))
throw new ServiceException("用户名不能为空");
if(StringUtils.isEmpty(entity.getPassword()))
throw new ServiceException("密码不能为空");
//2.将数据写入数据库
String salt=UUID.randomUUID().toString();
entity.setSalt(salt);
//加密(先了解,讲shiro时再说)
SimpleHash sHash=
new SimpleHash("MD5",entity.getPassword(), salt);
entity.setPassword(sHash.toString());
int rows=sysUserDao.insertObject(entity);
sysUserRoleDao.insertObject(
entity.getId(),
roleIds);//"1,2,3,4";
//3.返回结果
return rows;
}

说明:使用SimpleHash时,要添加一个shiro框架依赖

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>1.3.2</version>

</dependency>

27.用户Controller实现

业务实现

  1. 获取请求数据(用户信息,关系数据)

  2. 调用业务层方法将数据写入到数据库

  3. 封装数据,并返回

业务实现

  1. 方法名 doSaveObject

  2. 参数名(SysUser entity,String roleIds)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doSaveObject");

代码实现:

@RequestMapping("doSaveObject")
@ResponseBody
public JsonResult doSaveObject(
SysUser entity,
Integer[] roleIds){
sysUserService.saveObject(entity,roleIds);
return new JsonResult("save ok");
}

12.客户端实现

28.用户编辑页面事件处理

业务描述

  1. cancel事件处理(点击cancel按钮退出编辑页面加载列表页面)

  2. save 事件处理(将业务数据提交到服务端,并关闭当页面,加载列表页面)

代码实现:

Cancel按钮事件处理

$(document).ready(function(){
doLoadSysRoles();

$(".box-footer").on("click",".btn-cancel",doCancel);
});
function doCancel(){

$("#mainContentId").load("user/doUserListUI.do",function(){

$("#mainContentId").removeData();

});

}

save按钮事件处理

function doSaveOrUpdate(){
var rowData=$("#mainContentId").data("rowData");
//1.获取表单数据
var params=doGetEditFormData();
if(rowData)params.id=rowData.user.id;
//2.发起异步请求
var insertUrl="user/doSaveObject.do";
var updateUrl="user/doUpdateObject.do";
var url=rowData?updateUrl:insertUrl;
console.log(params);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
})
}

获取页面表单数据

function doGetEditFormData(){
//获取用户输入的数据
var params={
username:$("#usernameId").val(),
password:$("#passwordId").val(),
email:$("#emailId").val(),
mobile:$("#phoneId").val(),
deptId:$("#deptId").data("deptId")
}
//获取选择的角色
var roleIds=new Array();
$("#rolesId input[name='roleId']")
.each(function(){
if($(this).prop("checked")){
roleIds.push($(this).val());
}
});
params.roleIds=roleIds.toString();
return params;
}

7.用户页面修改呈现

核心业务:

1.根据用户id查找用户以及对应的角色信息

       a.方案1:执行两次查询(本阶段采用)

       b.方案2:执行一次关联查询(后续扩展)

2.验证此用户是否还存在

3.假如存在则将信息呈现在编辑页面上.


13.服务端实现

29.用户DAO实现

业务描述

根据用户id查询用户信息

业务实现(在SysUserDao中定义根据id查询用户信息的方法)

  1. 方法名: findObjectById

  2. 参数列表:Integer id

  3. 返回值: SysUser

代码实现

SysUser findObjectById(Integer id);

 30.用户Mapper实现

业务描述

在SysUserMapper中添加根据id进行用户信息查找的相关元素

业务实现

  1. 元素名 select

  2. 元素id findObjectById

  3. 参数类型 int

  4. 结果类型 com.jt.sys.entity.SysUser

  5. sql定义 (select * from from sys_users where user_id=?)

代码实现:

<select id="findObjectById"
parameterType="int"
resultType=" com.jt.sys.entity.SysUser">
select *
from sys_users  
where id=#{id}    
</select>

31.用户角色DAO实现

业务描述

根据用户id查询角色id

业务实现(在SysUserRoleDao中定义相关方法)

  1. 方法名 findRoleIdsByUserId

  2. 参数列表 Integer userId

  3. 返回值 List<Integer>

代码实现

List<Integer> findRoleIdsByUserId(
Integer userId);

 32.用户角色Mapper实现

业务描述

在SysUserRoleMapper中定义基于用户id查找角色id的相关元素

业务实现

  1. 元素名   select

  2. 参数列表 int

  3. 结果类型 int

  4. SQL语句 (select role_id from sys_user_roles where user_id=?)

代码实现

<select id="findRoleIdsByUserId"
resultType="int">
select role_id
from sys_user_roles
where user_id=#{userId}
</select>

33.用户Service实现

业务描述

  1. 获取控制层数据(userId),并对其进行合法性验证

  2. 调用dao方法根据用户id查询用户信息以及对应的角色信息

  3. 对数据进行封装,并返回.

业务实现(在SysUserService接口及实现类中定义相关方法)

  1. 方法名 findObjectById

  2. 参数列表(Integer userId)

  3. 返回值 Map<String,Object>

代码实现:

@Override
public Map<String, Object> findObjectById(
Integer userId) {
//1.合法性验证
if(userId==null||userId<=0)
throw new ServiceException(
"参数数据不合法,userId="+userId);
//2.业务查询
SysUser user=
sysUserDao.findObjectById(userId);
if(user==null)
throw new ServiceException("此用户已经不存在");
List<Integer> roleIds=
sysUserRoleDao.findRoleIdsByUserId(userId);
//3.数据封装
Map<String,Object> map=new HashMap<>();
map.put("user", user);
map.put("roleIds", roleIds);
return map;
}

34.用户Controller实现

业务描述

  1. 接收客户端请求数据 (userId)

  2. 调用业务方法查询用户以及对应角色信息

  3. 封装数据,并返回

业务实现(在SysUserController中定义相关方法)

  1. 方法名 doFindObjectById

  2. 参数列表 (Integer userId)

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doFindObjectById");

代码实现

@RequestMapping("doFindObjectById")
@ResponseBody
public JsonResult doFindObjectById(
Integer userId){
Map<String,Object> map=
sysUserService.findObjectById(userId);
return new JsonResult(map, "query ok");
}

14.客户端实现

核心业务

  1. 点击修改时加载编辑页面

  2. 在编辑页面上呈现用户以及对应的额角色信息


35.用户列表页面业务实现

业务描述

  1. 修改按钮事件注册

  2. 定义事件处理函数(获取id,根据id执行查询,绑定结果)

  3. 异步加载编辑页面.

代码实现:

$(document).ready(function(){
$(".input-group-btn")
.on("click",".btn-add,.btn-update",doLoadEditUI)
});
function doLoadEditUI(){
//1.判定点击的对象
var title;
if($(this).hasClass("btn-add")){
title="添加用户";
doLoadPage(title);
}else if($(this).hasClass("btn-update")){
title="修改用户";
var id=doGetCheckedId();
console.log("id="+id)
if(!id){
alert("请先选择");
return;
}
//基于id进行查询并加载编辑页面
doFindObjectById(id,title);
}
}

function doGetCheckedId(){return $("tbody input[name='radioId']:checked").val();   }
function doFindObjectById(id,title){
//1.params
var params={"id":id};
//2.url
var url="user/doFindObjectById.do";
//3.ajax request
$.getJSON(url,params,function(result){//JsonResult
if(result.state==1){
$("#mainContentId").data("rowData",result.data);
doLoadPage(title);
}else{
alert(result.message);
}
});
}

36.用户编辑页面数据初始化

页面描述

  1. 呈现页面,加载角色信息.

  2. 角色信息呈现结束,获取页面绑定的数据,通过数据初始化页面.

代码实现:

$(document).ready(function(){
doLoadRoles();
}

页面加载完成,加载角色信息

function doLoadRoles(){
var url="role/doFindObjects.do"
$.getJSON(url,function(result){
if(result.state==1){
doInitPageRoles(result.data);
doInitFormData();//修改时
}else{
alert(result.message);
}
})
}

修改时通过此方法初始化数据

function doInitPageRoles(data){
//1.获取角色要显示的位置对象
var div=$("#rolesId");
div.empty();
//2.迭代数据,将数据追加到div
var input="<input type='checkbox' name='roleId' value='[id]'>[name]"
for(var i in data){
//记住每次replace时都会产生一个新的字符串对象
var newInput=
input.replace("[id]",data[i].id)
.replace("[name]",data[i].name);
div.append(newInput)
}
}
function doInitFormData(){
var data=$("#mainContentId").data("rowData");
if(!data)return;
console.log(data);
//初始化用户信息
$("#usernameId").val(data.user.username);
$("#deptId").val(data.user.sysDept.name);
console.log("data.user.sysDept.idss="+data.user.sysDept.id);
$("#deptId").data("deptId",data.user.sysDept.id);
$("#emailId").val(data.user.email);
$("#phoneId").val(data.user.mobile);
//初始化用户角色信息
var ids=data.roleIds;
for(var i in ids){
$("#rolesId input[value='"+ids[i]+"']")
.prop("checked",true);
}
}

8.用户编辑页面数据更新

核心业务

  1. 获取用户表单数据

  2. 将表单数据更新到数据库.


15.服务端实现

37.用户Dao实现

业务描述

  1. 接收业务层数据(SysUser)

  2. 将数据持久化到数据库.

业务实现(在SysUserDao中添加更新数据的方法)

  1. 方法名 updateObject

  2. 参数列表 (SysUser user)

  3. 返回值 int

代码实现:

int updateObject(SysUser entity);

 38.用户Mapper实现

业务描述

基于SysUserDao中的更新方法,定义更新元素

元素定义

  1. 元素名 updateObject

  2. 参数类型  com.jt.sys.entity.SysUser

  3. sql 语句(update sys_users set ... where id=?)

代码实现:

<update id="updateObject" parameterType="sysUser">
update sys_users
<set>
<if test="username!=null and username!=''">
username=#{username},
</if>
<if test="password!=null and password!=''">
password=#{password},
salt=#{salt},
</if>
<if test="email!=null and email!=''">
email=#{email},
</if>
<if test="mobile!=null and mobile!=''">
mobile=#{mobile},
</if>
<if test="modifiedUser!=null and modifiedUser!=''">
modifiedUser=#{modifiedUser},
</if>
modifiedTime=now()
</set>
where id=#{id}     
</update>

39.用户角色Dao实现

页面描述

更新用户数据时,涉及到的关系数据,一般先删除,再添加,所以

在关系表对应的dao中我们需要先根据用户id删除对应的

用户角色关系数据.

业务实现(在SysUserRoleDao中定义删除方法)

  1. 方法名: deleteObjects;

  2. 参数列表: (Integer userId);

  3. 返回值: int

代码实现:

int deleteObjects(Integer userId);

40.用户角色Mapper实现

业务描述

基于SysUserRoleDao接口中删除方法,定义对应的删除元素.

业务实现

  1. 元素名: delete

  2. 元素id deleteObjects

  3. 参数:int

  4. sql定义: delete from sys_user_roles where user_id=?

代码实现:

<delete id="deleteObjects"
parameterType="int">
delete from sys_user_roles
where user_id=#{userId}
</delete>

41.用户Service实现

业务描述

  1. 获取控制层数据,并进行合法验证.

  2. 更新用户自身信息

  3. 删除用户角色关系数据

  4. 重新添加新的用户角色关系数据

业务实现:(在SysUserService接口及实现类中定义相关方法)

  1. 方法名: updateObject

  2. 参数列表:(SysUser entity,String roleIds)

  3. 返回值: int

代码实现:

@Override
public int updateObject(SysUser entity,
String roleIds) {
//1.合法验证
if(entity==null)
throw new ServiceException("用户信息不能为空");
if(StringUtils.isEmpty(entity.getUsername()))
throw new ServiceException("用户名不能为空");
//用户名已经存在的验证,尝试自己实现.
if(StringUtils.isEmpty(roleIds))
throw new ServiceException("用户必须选一个角色");
if(!StringUtils.isEmpty(entity.getPassword())){
//对密码加密
String salt=UUID.randomUUID().toString();
SimpleHash hash=//shiro
new SimpleHash(
"MD5",
entity.getPassword(),
salt);
entity.setPassword(hash.toString());
}
//2.更新数据
int rows=0;
try{
rows=sysUserDao.updateObject(entity);
sysUserRoleDao.deleteObjects(entity.getId());
sysUserRoleDao.insertObject(
entity.getId(),roleIds.split(","));
}catch(Throwable e){
e.printStackTrace();
//发起报警信息
throw new ServiceException("服务端现在异常,请稍后访问");
}
//3.返回结果
return rows;
}

42.用户Controller实现

业务描述

  1. 接收客户端请求数据

  2. 调用业务层方法,将请求数据更新到数据库

  3. 封装数据并返回.

业务实现:(在SysUserController中定义相关方法)

  1. 方法名 doUpdateObject

  2. 参数列表(SysUser entity,String roleIds);

  3. 返回值 JsonResult

  4. 映射 @RequestMapping("doUpdateObject")

代码实现:

@RequestMapping("doUpdateObject")
@ResponseBody
public JsonResult doUpdateObject(
SysUser entity,String roleIds){
sysUserService.updateObject(entity,
roleIds);
return new JsonResult("update ok");
}

16.客户端实现

核心业务

点击保存按钮时,将用户输入的表单数据,更新到数据库.


43.用户编辑页面事件处理.

业务描述

1)添加和更新操作共用一个页面.

2)点击保存按钮将数据更新到服务端.

代码实现.

function doSaveOrUpdate(){
var rowData=$("#mainContentId").data("rowData");
//1.获取表单数据
var params=doGetEditFormData();
if(rowData)params.id=rowData.user.id;
//2.发起异步请求
var insertUrl="user/doSaveObject.do";
var updateUrl="user/doUpdateObject.do";
var url=rowData?updateUrl:insertUrl;
console.log(params);
$.post(url,params,function(result){
if(result.state==1){
alert(result.message);
doCancel();
}else{
alert(result.message);
}
})
}

function doGetEditFormData(){
//获取用户输入的数据
var params={
username:$("#usernameId").val(),
password:$("#passwordId").val(),
email:$("#emailId").val(),
mobile:$("#phoneId").val(),
deptId:$("#deptId").data("deptId")
}
//获取选择的角色
var roleIds=new Array();
$("#rolesId input[name='roleId']")
.each(function(){
if($(this).prop("checked")){
roleIds.push($(this).val());
}
});
params.roleIds=roleIds.toString();
return params;
}

function doCancel(){
$("#mainContentId").removeData("id");
$("#mainContentId").load("user/doUserListUI.do");
}

9.总结

17.重点和难点分析

用户与角色关系数据的插入

用户与角色关系数据的更新

用户密码的加密设置


18.常见FAQ


 1

                                          齐雷


day06:京淘-权限管理子系统-Shiro安全框架

1.    Shiro安全框架简介                                       2

1.1.    Shiro 概述                                       2

1.2.    Shiro 概要架构                                    2

1.3.    Shiro 详细架构                                    2

2.    Shiro 认证与授权分析                                    4

2.4.    Shiro 认证流程                                    4

2.5.    Shiro 授权流程                                    4

3.    Shiro 框架基本配置                                      5

3.6.    Shiro 基本依赖配置                                5

3.7.    Shiro 基本组件配置                                6

3.8.    Shiro 核心过滤器配置                              7

4.    Shiro 框架认证流程应用实现                               8

4.9.    服务端实现                                        8

4.9.1.    Dao接口实现                                 8

4.9.2.    Mapper元素定义                              8

4.9.3.    Service 接口实现                            9

4.9.4.    Controller类实现                           10

4.10.    客户端实现                                      11

4.10.5.    编写用户登陆页面                           11

4.10.6.    异步登陆操作实现                           11

5.    Shiro 框架授权流程应用实现                              12

5.11.    服务端实现                                      12

5.11.7.    Dao 实现                                  12

5.11.8.    Mapper实现                                13

5.11.9.    Service实现                               14

5.12.    授权检测实现                                    15

6.    Shiro框架应用增强                                      16

6.12.10.    Shiro 缓存配置                           16

6.12.11.    Shiro 记住我                             16

7.    总结                                                  17

7.13.    重点和难点分析                                  17

7.14.    常见FAQ                                        17


  1. Shiro安全框架简介

    1. Shiro 概述

Shiro是apache旗下一个轻量级开源安全框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,授权、加密、会话管理等功能,组成了一个通用的安全认证框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统开发成本。

课后了解:Spring security 安全框架(相对shiro表现的更加重量级)


2.Shiro 概要架构

在概念层,Shiro 架构包含三个主要的理念:

Subject,SecurityManager和 Realm。

概要分析:

1)Subject: 待认证和授权的用户主题(例如封装了用户和密码的对象)

2)SecurityManager: Shiro的核心安全管理器对象

3)Realm:负责获取认证信息,授权信息的一个业务对象.


3.Shiro 详细架构

Shiro 的核心架构思想如下图所示:

通过Shiro框架进行权限管理时,要涉及到的一些核心对象,主要包括:

认证管理对象,授权管理对象,会话管理对象,缓存管理对象,加密管理对象

以及Realm管理对象(领域对象:负责处理认证和授权领域的数据访问题)

  1. Subject:与软件交互的一个特定的实体(用户、第三方服务等)。

  2. SecurityManager :Shiro 的核心,用来协调管理组件工作。

  3. Authenticator:负责执行认证操作

  4. Authorizer:负责授权检测

  5. SessionManager:负责创建并管理用户 Session 生命周期,提供一个强有力的 Session 体验。

  6. SessionDAO:代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。

  7. CacheManager:提供创建缓存实例和管理缓存生命周期的功能

  8. Cryptography:提供了加密方式的设计及管理。

  9. Realms:是shiro和你的应用程序安全数据之间的桥梁。


2.Shiro 认证与授权分析

4.Shiro 认证流程

用户访问系统资源时假如这个资源不能匿名访问就需要此用户进行身份认证,在Shiro框中的认证流程如下:

具体流程分析如下:

1)系统调用subject的login方法将用户信息提交给SecurityManager

2)SecurityManager将认证操作委托给认证器对象Authenticator

3)Authenticator借助认证策略对象将身份信息传递给Realm。

4)Realm访问数据库获取用户信息然后对信息进行封装并返回。

5)Authenticator对象对realm返回的信息进行身份认证。


5.Shiro 授权流程

用户访问系统资源时,首先要检测此资源是否允许匿名访问,假如不允许访问则需要先对用户信息进行认证,认证通过以后能否访问对应的资源还要看是否有访问权限,对用户权限进行检测并授予访问权限的过程称之为授权.Shiro框架中的授权流程如下:

1)系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager

2)SecurityManager将权限检测操作委托给Authorizer对象

3)Authorizer将用户对应权限信息的获取委托给realm对象

4)Realm访问数据库获取用户权限信息并封装返回。

5)Authorizer对用户授权信息进行判定并授权。



3.Shiro 框架基本配置

6.Shiro 基本依赖配置

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>

 7.Shiro 基本组件配置

Spring 集成shiro框架的基本应用,例如在AppRootConfig配置类中添加如下配置。

/**
* 配置shiro核心权限管理对象 
* @param userRealm 
* @return 
*/
@Bean("securityManager")
public DefaultWebSecurityManager  newDefaultWebSecurityManager(
AuthorizingRealm userRealm){
DefaultWebSecurityManager sManager=
new DefaultWebSecurityManager();
//此时必须保证realm对象已经存在了
sManager.setRealm(userRealm);
return sManager;
}
手动引入这个包
import org.apache.shiro.mgt.SecurityManager;
/**
* 配置Shiro的过滤器Bean工厂
* @param securityManager
* @return
*/
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean newShiroFilterFactoryBean(
SecurityManager securityManager){//shiro 包
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
//当此用户是一个非认证用户,需要先登陆进行认证
bean.setLoginUrl("/doLoginUI.do");
LinkedHashMap<String,String> fcMap=
new LinkedHashMap<>();
fcMap.put("/bower_components/**","anon");//anon表示允许匿名访问
fcMap.put("/build/**", "anon");
fcMap.put("/dist/**","anon");
fcMap.put("/plugins/**","anon");
fcMap.put("/doLogin.do","anon");
fcMap.put("/doLogout.do ","logout");
fcMap.put("/**", "authc");//必须授权才能访问
bean.setFilterChainDefinitionMap(fcMap);
return bean;
}
/**
* 管理shiro框架组建的生命周期
* @return
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor newLifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 配置负责为Bean对象(需要授权访问的方法所在的对象)
* 创建代理对象的Bean组件
* @return
*/
@DependsOn(value="lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator newDefaultAdvisorAutoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}
/**
* 配置授权属性属性应用对象(在执行授权操作时需要用到此对象)
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor newAuthorizationAttributeSourceAdvisor(
SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor bean=
new AuthorizationAttributeSourceAdvisor();
bean.setSecurityManager(securityManager);
return bean;
}
/**
* @PropertySource("classpath:configs.properties")
* 这种文件的注入方式Spring只会加载一次properties文件。当引入Shiro
* 并对其进行配置之后,由于Shiro的底层含有properties文件,Spring
* 可能先构建Properties对象加载了这个文件,而一旦是Spring识别到内
* 部含有Properties对象,就不会再加载其余的properties文件.所以
* jdbc的相关配置并未被加载,进而导致数据库连接异常。
*
* 解决方式是用Spring整合PropertySourcesPlaceholderConfigurer对象,
* 启用此对象可以加载多个properties文件。
* @return
*/
@Bean
public PropertySourcesPlaceholderConfigurer getPropertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}

8.Shiro 核心过滤器配置

在注解启动类中com/jt/common/config/WebAppInitializer.java,

重写onStartup方法,完成过滤器的注册

@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
System.out.println("onStartup");
//super.onStartup(servletContext);
registerContextLoaderListener(servletContext);
registerFilter(servletContext);
registerDispatcherServlet(servletContext);
}
private void registerFilter(ServletContext servletContext) {
//注册Filter对象
//什么时候需要采用此方式进行注册?
//项目没有web.xml并且此filter不是自己写的
FilterRegistration.Dynamic dy=
servletContext.addFilter("filterProxy",
DelegatingFilterProxy.class);
dy.setInitParameter("targetBeanName","shiroFilterFactoryBean");
dy.addMappingForUrlPatterns(
null,//EnumSet<DispatcherType>
false,"/*");//url-pattern
}

4.密码错误异常

com/jt/common/controller/GlobalExceptionHandler.java类中增加以下方法

/**
* 密码异常
* @param e
* @return
*/
@ExceptionHandler(AuthenticationException.class)
@ResponseBody
public JsonResult doHandleAuthenticationException(
AuthenticationException e){
if(e instanceof IncorrectCredentialsException) {
return new JsonResult(0,"密码不正确");
}
return new JsonResult(e);
}

com.jt.common.vo.JsonResult类中增加以下构造方法

/**密码异常时时调用*/
public JsonResult(Integer state ,String message){
this.state=state;
this.message=message;
}

效果:

出现的bug

修改后的用户密码还是无法登陆?

更改密码时将随机生成的盐值也添加到用户对象中

@Override
public int updateObject(SysUser entity, Integer[] roleIds) {
//1.校验
if(entity==null) throw new IllegalArgumentException("更新对象不能为空");
if(StringUtils.isEmpty(entity.getUsername())) throw new IllegalArgumentException("用户名不能为空");
if(roleIds==null||roleIds.length==0) throw new IllegalArgumentException("需要为用户分配角色");

if(!StringUtils.isEmpty(entity.getPassword())){
String salt=UUID.randomUUID().toString();
entity.setSalt(salt);
SimpleHash sh= new SimpleHash("MD5",entity.getPassword(),salt);
entity.setPassword(sh.toString());
}
//2.更新
int rows=sysUserDao.updateObject(entity);
sysUserRoleDao.deleteObjectsByUserId(entity.getId());
sysUserRoleDao.insertObject(entity.getId(), roleIds);
//3.返回
return rows;
}

4.Shiro 框架认证流程应用实现

本讲的shiro应用主要讲解shiro是集成到spring如何实现权限控制


9.服务端实现

  1. Dao接口实现

业务描述

在SysUserDao中根据用户名获取用户对象

业务实现(根据用户名查询用户对象的方法定义)

  1. 返回值SysUser

  2. 方法名findUserByUserName

  3. 参数列表(String username)

代码实现:

SysUser findUserByUserName(String username);

 2.Mapper元素定义

根据SysUserDao中定义的方法,添加元素定义

<select id="findUserByUserName"
resultType="sysUser">
select *
from sys_users 
where username=#{username}
</select>

 3.Service 接口实现

业务描述

本模块的service可以借助realm实现,我们编写realm时可以继承AuthorizingRealm并重写相关方法完成相关业务的实现。

业务实现:(创建realm类并重写相关方法)

  1. 包名:com.jt.sys.service.realm.ShiroUserRealm

  2. 类名:ShiroUserRealm

  3. 方法:AuthenticationInfo (完成认证信息的获取与封装)

@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserDao sysUserDao;
/**
* 设置凭证匹配器
* @param credentialsMatcher
*/
@Override
public void setCredentialsMatcher(
CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher cMatcher=
new HashedCredentialsMatcher();
cMatcher.setHashAlgorithmName("MD5");
super.setCredentialsMatcher(cMatcher);
}
/**
* 执行认证操作时,此方法用户获取用户认证信息
* 说明:此方法由认证管理器调用
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token)throws AuthenticationException {
//1.获取客户端提交的用户信息
UsernamePasswordToken upToken=
(UsernamePasswordToken)token;
String username=upToken.getUsername();
//2.基于用户名从数据库查询用户信息
SysUser user=sysUserDao.findUserByUserName(username);
//3.校验用户信息(用户存在吗)
if(user==null) throw new AuthenticationException("用户不存在");
if(user.getValid()==0) throw new AuthenticationException("此用户被禁用");
//4.对用户信息进行封装
ByteSource credentialsSalt=
ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info=
new SimpleAuthenticationInfo(
user, //principal(用户身份)
user.getPassword(),//hashedCredentials(已经加密的密码)
credentialsSalt, //credentialsSalt(盐)
this.getName());//realm name
return info;//此对象返回给谁了?认证管理器
}
/**
* 执行授权操作时,此方法用于获取用户的权限信息
* 负责完成用户权限领域的信息的获取及封装
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
//完成授权操作时需要在此方法完成数据获取与封装
//。。。。。。。。
System.out.println("============doGetAuthorizationInfo=========");
SysUser user = (SysUser) principals.getPrimaryPrincipal();
//基于用户id查找角色id信息
List<Integer> roleIds =  sysUserRoleDao.findRoleIdsByUserId(user.getId());
//基于用户id查找菜单id信息
Integer [] array = {};
List<Integer> menuIds= sysRoleMenuDao.findMenuIdsByRoleId(roleIds.toArray(array));
//基于菜单id查找权限标识信息
List<String> permisssions= sysMenuDao.findPermissions(menuIds.toArray(array));
//对权限标识进行去重和空操作
Set<String> pSet= new HashSet<String>();
for(String permission:permisssions){
if(!StringUtils.isEmpty(permission)){
pSet.add(permission);
}
}
//去重和空(null),空串
System.out.println("pSet="+pSet);
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.setStringPermissions(pSet);
return info;
}
}

4.Controller类实现

业务描述

  1. 在SysUserService接口及实现类中添加登录方法

  2. 接收用户名及密码参数,并对其进行有效验证

  3. 执行登录认证

代码实现

@RequestMapping("/")
@Controller
public class SysLoginController {
@RequestMapping("doLoginUI")
public String doLoginUI(){
return "login";
}
@RequestMapping("doLogin")
@ResponseBody
public JsonResult doLogin(String username,String password){
//1.获取Subject对象
Subject subject=SecurityUtils.getSubject();
//2.通过Subject提交用户信息,交给shiro框架进行认证操作
//2.1对用户进行封装
UsernamePasswordToken token=
new UsernamePasswordToken(
username,//身份信息
password);//凭证信息
//2.2对用户信息进行身份认证
subject.login(token);
//分析:
//1)token会传给shiro的SecurityManager
//2)SecurityManager将token传递给认证管理器
//3)认证管理器会将token传递给realm
return new JsonResult("login ok");
}
}

10.客户端实现

5.编写用户登陆页面

在WEB-INF/pages/目录下添加登陆页面(login.html)


6.异步登陆操作实现

$(function () {
$(".login-box-body").on("click",".btn",doLogin);
});
function doLogin(){
var params={
username:$("#usernameId").val(),
password:$("#passwordId").val()
}
var url="doLogin.do";
$.post(url,params,function(result){
if(result.state==1){
//跳转到indexUI对应的页面
location.href="doIndexUI.do?t="+Math.random();
}else{
$(".login-box-msg").html(result.message);
}
});
}

5.Shiro 框架授权流程应用实现

11.服务端实现

7.Dao 实现

业务描述:(核心业务是基于用户id获取用户对应的权限)

  1. 基于用户id查找角色id信息

  2. 基于角色id查找菜单id信息

  3. 基于菜单id查找权限标识信息

业务实现:(在SysUserRoleDao中基于用户id查找角色id信息)

  1. 返回值 List<Integer>

  2. 方法名 findRoleIdsByUserId

  3. 参数列表(Integer id)

业务实现:(在SysRoleMenuDao中基于用户id查找菜单id信息)

  1. 返回值 List<Integer>

  2. 方法名 findMenuIdsByRoleId

  3. 参数列表(Integer[] id)

业务实现:(在SysMenuDao中基于菜单id查找权限标识信息)

  1. 返回值 List<String>

  2. 方法名 findPermisssions

  3. 参数列表(Integer[] id)

代码实现:

SysUserRoleDao 中方法定义

List<Integer> findRoleIdsByUserId(
Integer id);

SysRoleMenuDao中方法定义

List<Integer> findMenuIdsByRoleId(
@Param("roleIds")Integer...roleIds);

SysMenuDao中方法定义

List<String> findPermissions(
@Param("menuIds")
Integer... menuIds);

 8.Mapper实现

业务描述

基于Dao中方法,定义映射元素

代码实现:

SysUserRoleMapper中元素定义

<select id="findRoleIdsByUserId"
resultType="int">
select role_id
from sys_user_roles
where user_id=#{userId}       
</select>

SysRoleMenuMapper中元素定义

<select id="findMenuIdsByRoleId"
resultType="int">
select menu_id
from sys_role_menus
where role_id in
<foreach collection="roleIds"
open="("
close=")"
separator=","
item="item">
#{item}
</foreach>
</select>

SysMenuMapper中元素定义

<select id="findPermissions"
resultType="string">
select permission <!-- sys:user:update -->
from sys_menus
where id in
<foreach collection="menuIds"
open="("
close=")"
separator=","
item="item">
#{item}
</foreach>
</select>

9.Service实现

业务描述

重写对象realm的doGetAuthorizationInfo方法,并完成用户权限信息的获取以及封装,最后将信息传递给授权管理器完成授权操作。

@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserRoleDao sysUserRoleDao;
@Autowired
private SysRoleMenuDao sysRoleMenuDao;
@Autowired
private SysMenuDao sysMenuDao;
……
/**执行授权操作时,此方法用于获取用户的权限信息*/
@Override
protected AuthorizationInfo
doGetAuthorizationInfo(
PrincipalCollection principals) {
System.out.println("==AuthorizationInfo==");
//1.获取用户对象(此对应依赖与认证时封装的用户身份)
SysUser user=(SysUser)
principals.getPrimaryPrincipal();
//2.基于用户id查找角色Id
List<Integer> roleIds=
sysUserRoleDao.findRoleIdsByUserId(
user.getId());
//3.基于角色id查找菜单(资源)id
Integer[] array={};
List<Integer> menuIds=
sysRoleMenuDao.findMenuIdsByRoleId(
roleIds.toArray(array));
//4.基于菜单id查找权限标识
List<String> permisssions=
sysMenuDao.findPermissions(
menuIds.toArray(array));
//5.封装权限信息(AuthorizationInfo)
Set<String> pSet=
new HashSet<String>();
for(String permission:permisssions){
if(!StringUtils.isEmpty(permission)){
pSet.add(permission);
}
}//去重和空(null),空串
System.out.println("pSet="+pSet);
SimpleAuthorizationInfo info=
new SimpleAuthorizationInfo();
info.setStringPermissions(pSet);
return info;
}
}

12.授权检测实现

在 com/jt/sys/service/impl/SysUserServiceImpl.java

需要进行授权检测的方法上添加执行此方法需要的权限标识

例如

/**
* 此方法需要权限检测需要添加shiro框架中的
* @RequiresPermissions
* 当底层系统运行时检测到方法上使用了
* @RequiresPermissions 注解就会为业务
* 对象创建一个代理对象,然后在代理对象中调用
* subject.isPermitted("sys:user:valid")
* 方法进行权限检测操作
*com/jt/sys/service/impl/SysUserServiceImpl.java
*/
@RequiresPermissions("sys:user:valid")
@Override
public int validById(Integer id,
Integer valid,
String modifiedUser) {
if(id==null||id<1)
throw new IllegalArgumentException("id值无效");
if(valid==null||(valid!=1&&valid!=0))
throw new IllegalArgumentException("状态值无效");
int rows=sysUserDao.validById(id,valid,modifiedUser);
if(rows==0)
throw new ServiceException("记录可能已经不存在");
return rows;
}

添加启用禁用菜单:

1、用admin用户登陆,点击菜单管理

6.无权限异常处理

在/common/controller/GlobalExceptionHandler.java类中

更改doHandleShiroException方法

/**
* 密码异常操作处理
* @param e
* @return
*/
@ExceptionHandler(ShiroException.class)
@ResponseBody
public JsonResult doHandleShiroException(ShiroException e){
if(e instanceof IncorrectCredentialsException) {
return new JsonResult(0,"密码不正确");
}else if(e instanceof AuthorizationException) {
return new JsonResult(0,"没有权限");
}
return new JsonResult(e);
}

效果:

 7、获取登陆用户名使修改信息可见:

在com/jt/sys/controller/SysUserController.java类中修改以下方法

@RequestMapping("doSaveObject")     
@ResponseBody
public JsonResult doSaveObject( SysUser entity, Integer[]roleIds){    
//获取登陆用户信息        
SysUser user = (SysUser)
SecurityUtils.getSubject().getPrincipal();
entity.setCreatedUser(user.getUsername());     
entity.setModifiedUser(user.getUsername());      
sysUserService.saveObject(entity, roleIds);    
return new JsonResult("save ok"); 
}
@RequestMapping("doValidById")
@ResponseBody
public JsonResult doValidById(Integer id,Integer valid){
System.out.println(sysUserService.getClass().getName());
//获取登陆用户信息
SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
sysUserService.validById(id,valid,user.getUsername());
return new JsonResult("update ok");
}

6.Shiro框架应用增强

10.Shiro 缓存配置

Step01: 添加ehcache 依赖

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>

Step02: 添加ehcache 配置文件

在项目的src/main/resources目录下添加ehcache.xml

Step03:  Spring 中配置ehcache 配置文件

<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>

将cacheManager添加到securityManager中

<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<property name="realm" ref="userRealm"></property>
</bean>

11.Shiro 记住我

课后可自学


7.总结

13.重点和难点分析


7.常见FAQ

 bug-01:角色管理更新出错可能是这里参数问题

java/com/jt/sys/dao/SysRoleMenuDao.java 类中findMenuIdsByRoleId 方法

/**
* 基于角色查询菜单id的方法
* @param roleId
* @return
*
* 角色管理更新出错可能是这里参数问题
* (@Param("roleIds") Integer... roleIds);
*/
List<Integer> findMenuIdsByRoleId(Integer roleId);

 bug-02:添加用户输入框的数据没有清掉:

在user-edit.html中更改为注释的内容

function doCancel(){
//$("#mainContentId").removeData("rowData");
$("#mainContentId").removeData("id");
$("#mainContentId").load("user/doUserListUI.do");
}

 1

                                           齐雷 qilei@tedu.cn


 day07:京淘项目-权限管理子系统-Spring AOP

1.    Spring AOP 概述                                                                                                      2

1.1.    AOP是什么?                                                                                              2

1.2.    AOP要解决什么问题?                                                                              2

1.3.    AOP实际项目应用场景?                                                                          2

1.4.    AOP底层原理实现分析?                                                                          3

1.5.    AOP 相关术语                                                                                             3

2.    Spring AOP 编程基础                                                                                              4

2.6.    AOP 基本步骤                                                                                             4

2.7.    AOP 基于xml实现                                                                                        4

2.7.1.    创建项目添加依赖                                                                            4

2.7.2.    添加spring配置文件                                                                           5

2.7.3.    创建核心业务类                                                                                6

2.7.4.    创建日志处理类                                                                                6

2.7.5.    配置bean对象                                                                                     6

2.7.6.    编写测试类                                                                                        8

2.7.7.    原理分析                                                                                            8

2.8.    AOP 基于注解实现                                                                                     9

2.8.8.    创建maven 项目添加依赖                                                                 9

2.8.9.    创建核心业务类                                                                              10

2.8.10.    创建时间处理类                                                                            10

2.8.11.    配置AOP实现                                                                                11

2.8.12.    编写测试类                                                                                    12

3.    Spring AOP 编程增强                                                                                            13

3.9.    切面表达式增强                                                                                        13

3.9.13.    Bean表达式应用增强                                                                    14

3.9.14.    Within表达式应用增强                                                                  14

3.9.15.    annotation表达式应用增强                                                            14

3.9.16.    Execution表达式应用增强                                                             14

3.10.    切面通知增强                                                                                          15

3.10.17.    Xml方式通知配置增强                                                                15

3.10.18.    注解方式通知配置增强                                                              16

3.11.    切面执行顺序配置增强                                                                          17

3.11.19.    Xml方式配置执行顺序                                                                17

3.11.20.    注解方式配置执行顺序                                                              18

4.    总结                                                                                                                       19

4.12.    重点和难点分析                                                                                      19

4.13.    常见FAQ                                                                                                  19

4.14.    作业                                                                                                          19


  1. Spring AOP 概述

    1. AOP是什么?

AOP 是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善

实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面中包含一个一个动态过程(在对象运行时动态织入一些功能。)

面向切面应用案例:如图所示


 2.AOP要解决什么问题?

实际项目中通常会将系统两大部分:核心关注点和非核心关注点

思考?

编程过程中首先要完成的是什么?核心关注点(核心业务)

非核心关注点如何切入到系统中?硬编码(违背OCP),AOP(推荐)

AOP就是要在基于OCP(开闭原则)在不改变原有系统核心业务代码的基础上动态添加一些扩展功能。


3.AOP实际项目应用场景?

AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。


4.AOP底层原理实现分析?

AOP底层基于代理机制实现功能扩展:(了解)

  1. 假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)

  2. 假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。


5.AOP 相关术语

切面(aspect): 横切面对象,一般为一个具体类对象(可以借助@Aspect声明)

连接点(joinpoint):程序执行过程中某个特定的点,一般指被拦截到的的方法

切入点(pointcut):对连接点拦截内容的一种定义,一般可以理解为多个连接点的结合.

通知(Advice):在切面的某个特定连接点上执行的动作(扩展功能),例如before,after等

很晦涩难懂,多做例子,做完就会清晰。先可以按白话去理解。


2.Spring AOP 编程基础

6.AOP 基本步骤

step1:创建maven java 项目

step2:添加aop依赖

step3:配置aop 核心(基于xml,基于注解)

step4:定义核心业务(核心关注点):推荐先写接口再写实现类

step5:定义扩展业务(非核心关注点)

step6:基于配置实现非核心业务的切入

step7:编写测试类进行单元测试


7.AOP 基于xml实现

通过AOP为核心业务类添加日志处理


  1. 创建项目添加依赖

创建maven java 项目,项目名为CGB-SPRING-DAY04-AOP-01然后在pom文件中添加依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--
Spring AOP的实现依托于Aspect框架(AOP框架)
所以要引用1.8.5有问题 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

2.添加spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
</beans>

 3.创建核心业务类

创建接口

public interface HelloService {
void sayHello(String msg);
}

创建接口实现类

public class HelloServiceImpl implements HelloService {
public void sayHello(String msg) {
//假设这条语句是我们系统中的核心业务
System.out.println(msg);
}
}

4.创建日志处理类

后续会将此日志处理类定义为横切面,通过此横切面实现扩展业务

public class LoggingAspect {
public void beforeMethod(){
System.out.println("method start");
}
public void afterMethod(){
System.out.println("method end");
}
}

5.配置bean对象

<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 核心业务对象 -->
<bean id="helloService"
class="spring.beans.HelloServiceImpl"/>
<!-- 配置非核心业务对象(日志处理对象):切面 -->
<bean id="log"
class="spring.aop.LoggingAspect"/>
<!-- AOP配置(切入点,切面) --> 
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut
expression="within(spring.beans.HelloServiceImpl)"
id="logPointCut"/>
<!-- 配置日志处理 -->
<aop:aspect ref="log" >
<aop:before method="beforeMethod"
pointcut-ref="logPointCut"/>
<aop:after  method="afterMethod"
pointcut-ref="logPointCut"/>
</aop:aspect>
</aop:config>
</beans>

6.编写测试类

public class TestAOP01 {
private ClassPathXmlApplicationContext ctx;
@Before
public void init(){
ctx=new ClassPathXmlApplicationContext("spring-configs.xml");
}
@Test
public void testSayHello(){
HelloService helloService=
ctx.getBean("helloService",HelloService.class);
helloService.sayHello("cgb1712");
}
@After
public void destory(){
ctx.close();
}
}

7.原理分析

基于AOP应用,对其执行过程进行分析,如图所示


 8.AOP 基于注解实现

8.创建maven 项目添加依赖

创建MAVEN JAVA 项目CGB-SPRING-DAY20-AOP-02,然后在POM文件中添加依赖 

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--
Spring AOP的实现依托于Aspect框架
所以要引用1.8.5有问题
-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

9.创建核心业务类

定义一个订单接口,此接口中定义多个业务操作。

public interface OrderService {
public void saveOrder();
public void deleteOrder();
}

创建接口核心业务实现类,并使用@Service注解进行修饰


10.创建时间处理类

将此时间处理类作为核心业务增强(一个横切面对象)类,用于输出业务开始执行时间,以及业务结束执行时间。

横切面对象主要由两部分构成:切入点(用于@Pointcut标识),以及功能增强(用通知@Before,@After等进行标识)

@Aspect
@Service
public class TimeAspect {
@Pointcut("bean(orderService)")
public void pointcut(){}
/**增强功能:前置通知(业务方法执行之前执行)*/
@Before("pointcut()")
public void begin(){
System.out.println("start:"+System.nanoTime());
}
/**增强功能:最终通知(业务方法执行最后执行,
*无论业务方法是否执行成功,此功能都要执行)*/
@After("pointcut()")
public void end(){
System.out.println("end:"+System.nanoTime());
}
}

其中:

@Aspect 注解用于标识此类为一个AOP横切面对象

@Pointcut 注解用于定义本类中的切入点,本案例中切入点表达式用的是bean表达式,这个表达式以bean开头,bean括号中的内容为一个spring管理的某个bean对象的id。

@Before 用于定义一个前置通知(满足切入点表达式的核心业务方法执行之前要执行的一个操作)

@After  用于定义一个后置通知(满足切入点表达式的核心业务方法执行之后要执行的一个操作)

术语增强:

切面:用于封装扩展业务的一个类的对象。

通知:切面扩展业务中的一个操作。


11.配置AOP实现

对于基于注解方式的配置一般有两种方式,一种是直接在xml核心配置文件中进行配置,还有一种在类中基于注解进行配置。例如

基于xml方式配置对注解的应用

<?xml version="1.0" encoding="UTF-8"?>
<beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置对类组件的扫描 -->
<context:component-scan
base-package="com.spring"/>
<!-- 启用AOP注解(自动为目标对象创建代理对象) -->
<aop:aspectj-autoproxy/>
</beans>

在类中基于注解方式的配置

@ComponentScan("com.spring.beans")
@EnableAspectJAutoProxy
public class AppConfig {

}

12.编写测试类

基于xml方式注解配置的测试实现

public class TestAOP01 {
public static void main(String[] args) {
//1.初始化容器
ClassPathXmlApplicationContext ctx=
new ClassPathXmlApplicationContext(
"applicationContext.xml");
//2.获取bean对象
OrderService os=(OrderService)
ctx.getBean("orderService",
OrderService.class);
//3.执行业务方法
os.saveOrder();
os.deleteOrder();
//4.释放资源
ctx.close();
}
}

基于类中注解方式配置的测试实现

public class TestAOP02 {
public static void main(String[] args) {
//1.初始化容器对象
AnnotationConfigApplicationContext ctx=
new AnnotationConfigApplicationContext(
AppConfig.class);
//2.获取Bean对象
OrderService orderService=
ctx.getBean("orderService", OrderService.class);
//3.执行业务
orderService.saveOrder();
//orderService.deleteOrder();
//4.释放资源
ctx.close();
}
}

基于类的注解配置,初始化工厂时需要初始化

AnnotationConfigApplicationContext对象。


3.Spring AOP 编程增强

9.切面表达式增强

Spring中通过切入点表达式定义具体切入点,其常用AOP切入点表达式定义及说明:

指示符

作用

bean

用于匹配指定bean id的的方法执行

within

@annotation         

用于匹配指定包名下类型内的方法执行

用于匹配指定注解修饰的方法执行

execution

用于进行细粒度方法匹配执行具体业务


 13.Bean表达式应用增强

bean应用于类级别,实现粗粒度的控制:

bean("userServiceImpl"))

指定一个类

bean("*Service")

指定所有的后缀为service的类


14.Within表达式应用增强

within应用于类级别,实现粗粒度的切面表达式定义:

within("aop.service.UserServiceImpl")

指定类,只能指定一个类

within("aop.service.*")

只包括当前目录下的类

within("aop.service..*")

指定当前目录包含所有子目录中的类


15.annotation表达式应用增强

@anotation 应用于方法级别,实现细粒度的切面表达式定义:

@annotation("common.anno.RequestLog")

表示使用此注解修饰的方法要添加扩展功能


16.Execution表达式应用增强

execution方法级别,细粒度的控制:

语法:execution(返回值类型 包名.类名.方法名(参数列表))

execution(void aop.service.UserServiceImpl.addUser())

匹配方法

execution(void aop.service.PersonServiceImpl.addUser(String))

方法参数必须为字符串

execution(* aop.service..*.*(..))

万能配置


10.切面通知增强

在AOP编程中有五种类型的通知:

  1. 前置通知 (@Before)

  2. 返回通知 (@AfterReturning)

  3. 异常通知 (@AfterThrowing)

  4. 后置通知 (@After) : 又称之为最终通知(finally)

  5. 环绕通知 (@Around) :课后扩展了解


17.Xml方式通知配置增强

切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置

<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:before method="beforeMethod" pointcut-ref="pc"/>
<aop:after method="afterMethod" pointcut-ref="pc"/>
<aop:after-returning method="returnMethod"
pointcut-ref="pc"/>
<aop:after-throwing method="throwMethod"
pointcut-ref="pc"/>
</aop:aspect>
</aop:config>

切入点及环绕通知的配置(了解)

<aop:config>
<aop:pointcut id="pc"expression="execution(*
com.company.spring.service..*.*(..))" >
<aop:aspect ref="loggingAspect">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>

18.注解方式通知配置增强

切入点及前置通知,后置通知,返回通知,异常通知,环绕通知的配置

@Aspect
@Service
public class LogAspect {
@Pointcut("bean(orderServiceImpl)")
public void doLog(){}
@Before("doLog()")
public void doBefore(){
System.out.println("log before");
}
@After("doLog()")
public void doAfter(){
System.out.println("log after");
}
/**核心业务正常结束时执行
* 说明:假如有after,先执行after,再执行returning*/
@AfterReturning("doLog()")
public void doAfterReturning(){
System.out.println("log doAfterReturning");
}
/**核心业务出现异常时执行
说明:假如有after,先执行after,再执行Throwing*/
@AfterThrowing("doLog()")
public void doAfterThrowing(){
System.out.println("log doAfterThrowing");
}
}

切入点及环绕通知的配置(课后自己实现)

@Component
@Aspect
public class TxManager {
@Pointcut("execution(com.company.spring.service..*.*(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
try{
System.out.println("事务开始");
Object result = joinPoint.proceed();//执行目标方法
System.out.println("提交事务");
return result;
}catch(Exception e){
System.out.println("回滚事务");
throw e;
}finally{
System.out.println("释放资源");
}
}
}

11.切面执行顺序配置增强

实际项目中可能会有多个切面,切面之间的执行可能需要一定的顺序


19.Xml方式配置执行顺序

<aop:config>
<aop:pointcut id="pc"
expression="execution(*
com.company.spring.service..*.*(..))"/>
<aop:aspect ref="loggingAspect" order="1">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
<aop:aspect ref="txManager" order="2">
<aop:around method="aroundMethod" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>

20.注解方式配置执行顺序

注解方式顺序配置需要借助@Order注解

@Order(1)
@Aspect
@Component
public class TxManager {
@Pointcut("execution(* com.company.spring.service..*.(..))")
public void pointCut() {}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint)
throws Throwable{
System.out.println("事务开始");
Object result = joinPoint.proceed();
System.out.println("事务结束");
return result;
}
}

注解方式顺序配置

@Order(2)
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.company.spring.service..*.(..))")
public void pointCut() {}
@Before("pointCut()")
public void beforeMethod() {
System.out.println("beforeMethod");
}
@Before("pointCut()")
public void afterMethod() {
System.out.println("afterMethod");
}
}

4.总结

12.重点和难点分析

  1. AOP 是什么,解决了什么问题,实现原理,应用场景

  2. AOP 编程基本步骤及基本实现

  3. AOP 编程中重点掌握基于注解方式配置


13.常见FAQ

  1. 什么是OCP原则(开闭原则):允许扩展,不允许修改

  2. 什么是单一职责原则(一个类或接口的职责不要太多)

  3. spring中AOP的有哪些配置方式(xml和注解)

  4. Spring 中AOP 的通知有哪些基本类型

  5. Spring 中AOP是如何为Bean对象创建代理对象的?

4.作业

  1. 总结课堂知识点(笔记再详细也是别人的,不是自己的,拿来主义对自己的提高会很慢,无论是否认同,可以试试,看看最后效果)

  2. 完成课堂AOP基本案例

  3. 了解代理模式及应用场景,实现方式(最好自己尝试实现)

  4. 尝试自己基于JDK为某个类创建一个代理对象.(Proxy.newProxyInstance(...))

  5. 尝试自己基于CGLIB为某个类创建一个代理对象(依赖于cglib库)


1

                                                            齐雷


      京淘项目

  Spring 事务管理

1.    Spring 事务管理                                                                                                       1

1.1.    Spring 事务概述                                                                                           1

1.2.    Spring事务案例分析                                                                                    2

2.    Spring 声明式事务处理                                                                                           3

2.3.    基于注解方式实现                                                                                      3

2.4.    基于xml方式实现                                                                                         4

3.    Spring事务增强                                                                                                        5

3.5.    Spring 事务的传播特性                                                                               5

3.6.    Spring 事务的隔离级别                                                                               6

4.    总结                                                                                                                         8

4.7.    重点和难点分析                                                                                          8

4.8.    常见FAQ                                                                                                      8


  1. Spring 事务管理

    1. Spring 事务概述

事务是一个不可分割的逻辑工作单元,具备ACID特性,实际工作中可借助Spring进行事务管理。

事务四大特性:ACID

  1. 原型子型(一个事务中的多个操作要么都成功要么都失败)

  2. 一致性(例如存钱操作,存之前和存之前的钱数应该是一致的)

  3. 隔离性(事务与事务应该是相互隔离的)

  4. 持久性(事务一旦提交,数据要持久保存)

Spring提供了两种事务管理方式, 编程式事务和声明式事务。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。

Spring中声明式事务处理有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional 注解的方式。

本讲重点讲解实际项目中最常用的声明式事务管理,以简化事务的编码操作。


2.Spring事务案例分析

例如现有两个订单操作,需要更新库存。

当库存充足时两个事务都可以成功,当库存不够时有的事务就要回滚。

说明:Spring声明式事务管理底层基于AOP实现


2.Spring 声明式事务处理

3.基于注解方式实现

Step1:在spring配置文件中启用事务注解

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--设置注解驱动的事务管理  -->
<tx:annotation-driven transaction-manager="txManager"/>

Step2:在类或方法中使用@Transaction注解应用事务。

  • value   当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。

  • propagation   事务的传播行为,默认值为 REQUIRED。

  • isolation  事务的隔离度,默认值采用 DEFAULT。

  • timeout    事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

  • read-only  指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

  • rollback-for  用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。

  • no-rollback- for  抛出 no-rollback-for 指定的异常类型,不回滚事务。

说明:@Transactional 注解可以用在方法上也可以添加到类级别上。当把@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。见清单 2,EmployeeService 的所有方法都支持事务并且是只读。当类级别配置了@Transactional,方法级别也配置了@Transactional,应用程序会以方法级别的事务属性信息来管理事务,换言之,方法级别的事务属性信息会覆盖类级别的相关配置信息。


4.基于xml方式实现

在配置文件中通过xml配置方式实现声明式事务管理。

配置事务管理器

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

配置事务处理方式

<tx:advice id="txAdvice"  transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"
propagation="REQUIRED"
isolation="READ_COMMITTED"
timeout="-1"
read-only="false"
rollback-for="java.lang.Throwable"
no-rollback-for=”NoTransactionException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="operation" expression="execution(* beans.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut="operation"/>
</aop:config>

课堂练习:

Step01:定义事务管理器

<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="query*" read-only="true" />
<tx:method name="find*" read-only="true" />
<tx:method name="get*" read-only="true" />
<!-- 主库执行操作,事务传播行为定义为默认行为 -->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<!--其他方法使用默认事务策略 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 定义切面,所有的service的所有方法 -->
<aop:pointcut id="txPointcut"
expression="execution(* com.jt.sys.service..*.*(..))" />
<!-- 应用事务策略到Service切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

4.基于注解方式实现

1、在com/jt/common/config/AppRootConfig.java类添加来配置事务管理器

@Bean("txManager")
public DataSourceTransactionManager newDataSourceTransactionManager(
@Autowired DataSource dataSource) {
DataSourceTransactionManager tManager=  new DataSourceTransactionManager();
tManager.setDataSource(dataSource);
return tManager;
}

2、设置注解驱动的事务管理

3、第三步

4、效果:

5、再查看数据库数据

并没有插入数据说明事物操作ok


 3.Spring事务增强

5.Spring 事务的传播特性

事务传播特性:事务方法之间相互调用时,事务的传播方式.

重点掌握 Propagation.REQUIRED

@Transactional(propagation=Propagation.REQUIRED)

 如果没有事务创建新事务, 如果当前有事务参与当前事务

@Transactional(propagation=Propagation.REQUIRES_NEW)

必须是新事务, 如果有当前事务, 挂起当前事务并且开启新事务.

课后了解:

@Transactional(propagation=Propagation.MANDATORY)必须有事务, 如果当前没有事务就抛异常

@Transactional(propagation=Propagation.NEVER)绝对不能有事务, 如果在事务中调用则抛出异常

@Transactional(propagation=Propagation.NESTED)必须被嵌套到其他事务中

@Transactional(propagation=Propagation.NOT_SUPPORTED)不支持事务

@Transactional(propagation=Propagation.SUPPORTS)支持事务, 如果没有事务也不会创建新事务


 6.Spring 事务的隔离级别

思考:多个事务并发执行时可能会导致什么问题?(脏读,不可重复读,幻读)

当多个事务并发执行时,可通过设置事务的隔离级别保证事务的完整性,一致性。

事务的隔离级别从低到高有如下几种方式:

1)READ_UNCOMMITTED (此级别可能会出现脏读)

2)READ_COMMITTED(此级别可能会出现不可重复读)

3)REPEATABLE_READ(此级别可能会出现幻读)

4)SERIALIZABLE(多事务串行执行)

说明:spring中一般采用 @Transactional(isolation=Isolation.READ_COMMITTED) 方式声明级别, 这种方式是并发性能和安全性折中的选择. 是大多数软件项目采用的隔离级别.

回顾MySQL中隔离级别:

查看InnoDB存储引擎 系统级的隔离级别 和 会话级的隔离级别

更改会话级的隔离级别

更改系统级的隔离级别

思考:

1)MySQL 中如何查看当前系统默认隔离级别?

show variables like '%storage_engine%';

2)MySQL 中如何设置事务隔离级别?

set session transaction isolation level 'reapable read


 


作者:Darren

QQ:603026148

以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从码农到码到成功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值