京淘权限子系统项总结
1.配置文件的设置
1.1 spring-configs.xml (spring的核心配置文件)
<!-- 自动扫描该包 -->
<context:component-scan base-package="com.jt" />
<!-- 启用mvc注解 -->
<mvc:annotation-driven />
<!-- 定义跳转的文件的前后缀 ,视图模式配置 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".html"></property>
</bean>
<!-- 整合DRUID -->
<util:properties id="cfg" location="classpath:config.properties"/>
<!--配置DruidDataSource连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" init-method="init" lazy-init="true">
<property name="driverClassName" value="#{cfg.driver}" />
<property name="url" value="#{cfg.url}" />
<property name="username" value="#{cfg.username}" />
<property name="password" value="#{cfg.password}" />
</bean>
<!-- 整合mybatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-configs.xml"></property>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" >
<list><value>classpath:mapper/*.xml</value></list>
</property>
</bean>
<!-- 扫描DAO接口所在包,Spring会自动查找其下的dao
接口,然后为接口创建代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="com.jt.**.dao"/>
</bean>
在整合mybatis的时候,可以在spring核心配置文件里面自动扫描mapper.xml文件了,是用SqlSessionFactoryBean的setMapperLocations属性,这样就省去了在mybatis-configs.xml中每一个都要引入新加入的mapper.xml文件了
<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-configs.xml"></property>
</bean>
像这种方法使用的是configLocation属性,就是引入mybatis的核心配置问价,里面再进行依然mapper.xml文件,但是这样做有一个弊端,就是不能模糊配置,
每一次加入mapper.xml文件都得重新添加
1.2 mybatis-configs.xml(mybatis的核心配置文件)
<configuration>
<settings>
<!-- mybatis控制台LOG输出 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.jt.sys.entity"/>
<package name="com.jt.common.vo"/>
</typeAliases>
</configuration>
这里面要使用log4j进行日志的控制,还需要进行别名的配置
1.3 如果使用注解开发的话,那么就不需要web.xml配置文件了
1.3.1(具体的做法看项目,已经之前的比价)
1.4有一点可以配置上.那就是pom.xml里面配置插件:(配置jdk编译和运行时环境的插件,那么在每一次maven-update的时候,就不需要改Project Facets)
具体的配法:
<build>
<plugins>
<!-- 添加maven编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<!-- 编译时环境 -->
<source>1.7</source>
<!-- 运行时环境 -->
<target>1.8</target>
</configuration>
</plugin>
</plugins>
2.日期到页面上格式的转换
日期在数据库存的是长整型的数据,但是要显示在页面上,得要是规定的容器格式,这种容器格式的转换有三种方法:
1.第一种方法:
*在实体类中的的容器属性的get方法上进行日期的转换,因为页面上进行数据的呈现,是通过实体类的get方法进行获取的时间的,方法:
public String getModifiedTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-hh");
return sdf.format(modifiedTime);
}
2.第二种方法:
*是页面利用js来实现:在追加列的时候,使用js的函数实现容器格式的转换.
"<td>"+new Date(records[i].createdTime).toLocaleDateString()+"</td>"
如果忘了这个函数怎么写,可以在浏览器F12进入控制台,进行测试
3.第三种方法:
*写一个容器转换类:
/**
* Spring 整合jackson以后,在将一个对象转换json串时,假如
* 有日期类型,默认会将日期类型转换为长整型的字符串,当我们
* 需要一个具体的日期格式字符串时,可以自己写个类继承
* JsonSerializer然后写serialize方法,在此方法完成
* 日期对象的转换,具体应用还需要在实体对象的get方法上
* 借助@JsonSerializer注解使用此类
*/
public class DateFormatConverter extends JsonSerializer<Date>{
//private SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
/**
* 负责相关对象的转换操作
* @param value 表示要转换的数据
* @param gen 负责将转换的数据写到json串中
*/
@Override
public void serialize(Date value,
JsonGenerator gen,
SerializerProvider serializers)
throws IOException,
JsonProcessingException {
//1.转换数据
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
String dateStr=sdf.format(value);
//2.将转换的结果写到json串中
gen.writeString(dateStr);
}
}
这是一个转换类,可以使用注解来实现功能:(这里可以定义一个实体类的父类,里面可以写实体类通用的属性,在里面做日期格式的转换,那么所有的子类的容器格式都转换了)
public class BaseEntity {
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
@JsonSerialize(using=DateFormatConverter.class)
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
@JsonSerialize(using=DateFormatConverter.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;
}
}
###这三种方式,最后一种方式,实现起来方法比较麻烦,但是写好了之后,以后容器格式的转换都可以通用的,不需要再重现写,###
对于SimpleDateFormat来讲又有一个知识点,那就是SimpleDateFormat是线程不安全的,解决办法取折中的办法就是使用ThreadLocal
3.自定义处理的异常
自定义的异常在一开始就要做好,因为只要编写了业务层,就要使用异常处理:
3.1:现在common.exception包中自定义一个异常,这个异常可以继承RuntimeException,生成构造方法,都是继承父类的构造方法:
/***
* 为什么使用自定义异常?
* 更加精确定位具体异常信息
*/
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = -1169100027771948958L;
public ServiceException() {
super();
}
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(String message) {
super(message);
}
public ServiceException(Throwable cause) {
super(cause);
}
}
3.2定义异常处理类:用来捕获异常,返回给页面:在common.controller包下定义ControllerExceptionHandler类:这是Spring MVC基于注解方式统一实现异常处理的方式:(异常处理完之后的信息是要返回给页面的,所以返回的结果类型是JsonResult)
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler(ServiceException.class)
@ResponseBody
public JsonResult handleServiceException(
RuntimeException e){
System.out.println("handleServiceException");
e.printStackTrace();
//封装错误信息
return new JsonResult(e);
}
@ExceptionHandler(AuthorizationException.class)
@ResponseBody
public JsonResult handleAuthorizationeException(
AuthorizationException e){
System.out.println("handleAuthorizationeException");
e.printStackTrace();
//封装错误信息
return new JsonResult(new ServiceException("没有此权限"));
}
//.......
}
4.定义一个实体类的父类,里面写父类的所有的共有的属性:BaseEntity
public class BaseEntity {
private Date createdTime;
private Date modifiedTime;
private String createdUser;
private String modifiedUser;
@JsonSerialize(using=DateFormatConverter.class)
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
@JsonSerialize(using=DateFormatConverter.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;
}
}
这个类的所有跟实现有关的get方法上都添加了@JsonSerialize注解,这样子类就不需要再进行日期的转换,这样处理起来非常方便,并且注意,这个父类是不要实现序列化接口,生成序列号的,让子类来实现就可以了.
5.Shrio框架的应用:(这一步应该放在比较后的位置来做(做笔记时,是为了体系化)
5.1添加相关的依赖:
<!-- 整合Shiro 安全框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 整合ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
5.2编写自定义的Realm类: 助此对象访问用户身份信息,权限信息并对 其进行封装,返回给调用者.这个对象先是有认证管理器来调用,然后由授权管理器来调用:
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired
private SysUserDao sysUserDao;
/***
* 完成授权信息的获取以及封装. 此方法何时调用?(执行授权检测时调用)
*
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("realm.doGetAuthorizationInfo");
// 1.获取登陆用户身份信息
String username = (String) principals.getPrimaryPrincipal();
// 2.查找用户的权限信息
List<String> list = // sys:user:update,sys:user:view,....,
sysUserDao.findUserPermissions(username);
System.out.println("list=" + list);
Set<String> permissions = new HashSet<>();
for (String permission : list) {
if (!StringUtils.isEmpty(permission)) {
permissions.add(permission);
}
}
System.out.println("set=" + permissions);
// 3.对权限信息进行封装
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permissions);
return info;
}
/**
* 完成认证信息的获取以及封装 此方法何时调用?(执行登陆认证时调用)
*
* @param 用于接收用户身份以及凭证信息的对象(用户输入的)
*
* @return AuthenticationInfo 封装了认证信息的对象(从数据库查询到的)
*
* client-->controller-->service-->realm
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("realm.doGetAuthenticationInfo");
// 1.获取用户身份信息
UsernamePasswordToken uToken = (UsernamePasswordToken) token;
String username = uToken.getUsername();
// 2.基于用户身份查询数据库信息
SysUser sysUser = sysUserDao.findUserByUserName(username);
// 3.对查询结果进行封装.
// 3.1获取用户salt值,并将其转换为一个字节源对象
ByteSource byteSource = ByteSource.Util.bytes(sysUser.getSalt());
// 3.2对用户信息进行封装返回.
AuthenticationInfo info = new SimpleAuthenticationInfo(sysUser.getUsername(), // 主身份 (可以将用户传过去)
sysUser.getPassword(), // 已加密的密码
byteSource, // salt对应的字节源对象
getName());// realm 的名字
return info;
}
}
这个来要完成的工作就是用户信息的认证,以及权限的检查,(先走的是认证的方法,然后走的是授权的方法)
授权方法,是要去更加根据用户名字查询当前用户对的权限的.
5.3编写spring的核心配置文件,需要交给spring来管理
<!-- 配置shiro框架-->
<!-- 配置realm对象(将给spring管理) -->
<bean id="userRealm"
class="com.jt.sys.service.realm.ShiroUserRealm">
<!-- 凭证匹配器(密码加密) -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<!-- <property name="hashIterations" value="1024"/> -->
</bean>
</property>
</bean>
<!-- 配置CacheManager对象(不是必须的,主要是为了提高性能,可以对认证信息以及授权信息进行缓存) -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- Set a net.sf.ehcache.CacheManager instance here if you already have one. If not, a new one
will be creaed with a default config:
<property name="cacheManager" ref="ehCacheManager"/> -->
<!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
a specific Ehcache configuration to be used, specify that here. If you don't, a default
will be used.: -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
<!-- 配置securityManager对象(此对象时shiro框架核心) -->
<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 配置ShiroFilter(通过此filter的配置实现对请求资源的过滤,哪些请求要放行,哪些要认证)-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的核心安全接口 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的连接 -->
<property name="loginUrl" value="/loginUI.do"></property>
<!-- 登录成功后要跳转的连接(此处已经在登录中处理了) -->
<!-- <property name="successUrl" value="/index.jsp"></property> -->
<!-- 访问未对其授权的资源时,要跳转的连接
<property name="unauthorizedUrl" value="/default.html"></property>-->
<!-- shiro连接约束配置 -->
<property name="filterChainDefinitions">
<value>
<!-- 对静态资源设置允许匿名访问 -->
/bower_components/** = anon
/build/** = anon
/dist/** = anon
/plugins/** = anon
/doLogin.do = anon
<!-- 退出 -->
/doLogout.do = logout <!-- 会调用Subject的logout方法,此方法会将session清空 -->
<!-- 剩余其他路径,必须认证通过才可以访问 -->
/** = authc
</value>
</property>
</bean>
<!--Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!--启用shiro注解权限检查(@RequestPermissions)-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
5.4编写web.xml
<!-- 配置Shiro安全过滤器 -->
<filter>
<filter-name>DelegatingFilterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 初始化参数 -->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DelegatingFilterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这样的配置是拦截所有的路径,一旦有路劲访问过来的,那么就要启用shiroFilter,就要进行拦截,检查
5.5编写登录的Controller和Service:
@Controller
@RequestMapping("/")
public class SysLoginController {
@Autowired
private SysUserService sysUserService;
@RequestMapping("loginUI")
public String loginUI(){
return "login";
}
@RequestMapping("doLogin")
@ResponseBody
public JsonResult doLogin(
String username,
String password){
String r=DigestUtils.md5DigestAsHex("123456".getBytes());
System.out.println("r="+r);
sysUserService.login(
username,
password);
return new JsonResult("login ok");
}
}
@Override
public void login(String username, String password) {
System.out.println(sysRoleDao.getClass().getName());
System.out.println("service.login");
// 0.参数合法性验证
if (StringUtils.isEmpty(username))
throw new ServiceException("用户名不能为空");
if (StringUtils.isEmpty(password))
throw new ServiceException("密码不能为空");
// 1.获取Subject(主体)对象
Subject subject = SecurityUtils.getSubject();
// 2.封装用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 3.执行身份认证
try {
subject.login(token);
// 此请求会提交给SecurityManager
// SecurityManager会调用认证处理器Authenticator
// 认证处理器会去访问相关Realm对象获取认证信息
} catch (AuthenticationException e) {
e.printStackTrace();
throw new ServiceException("用户名或密码不正确");
}
// 4.记录用户信息
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("user", username);
}
5.5添加权限:
// Subject.isPermitted("sys:user:valid")
// 当访问此方法时,假如有@RequiresPermissions
// 这个注解,表示此用户必须是授权用户,才能访问
@RequiresPermissions("sys:user:valid")
@Override
public int validById(Integer id, Integer valid) {
// 1.合法性验证
if (id == null || id < 1)
throw new ServiceException("数据不合法,id=" + id);
if (valid == null)
throw new ServiceException("状态值不能为空");
if (valid != 0 && valid != 1)
throw new ServiceException("状态值不正确,valid=" + valid);
// 2.执行更新操作
int rows;
try {
rows = sysUserDao.validById(id, valid);
} catch (Throwable e) {
e.printStackTrace();
// 报警
throw new ServiceException("系统维护中");
}
// 3.验证结果并处理
if (rows == 0)
throw new ServiceException("数据可能已经不存在");
return rows;
}
方法上加上@RequiredPermissions()注解就可以去检验权限了
6.事务的添加
6.1spring-configs.xml配置文件中进行配置:
<!-- 配置spring事务管理 -->
<!--1)定义事务管理对象(可以将此对象理解为切面) -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2)配置事务策略 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" read-only="true" isolation="READ_COMMITTED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="valid*" propagation="REQUIRED"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 3)配置事务应用(在哪些业务对象的哪些方法上使用事务) -->
<aop:config>
<aop:pointcut expression="execution(* com.jt.sys.service..*.*(..))"
id="txPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
具体细节看之前的自己以及老师的笔记:
前端业务代码实现的总结:
心得1:start.html是看到的首页,点击左侧的菜单可以跳转到别的页面:分别给这四个菜单绑定点击事件,这里用的是click()函数,因为这个四个菜单都有id,直接用click()函数更加方便一些;
$(function(){
$("#load-role-id").click(function(){
$(".container-fluid").load("role/listUI.do",function(){
$(".container-fluid").removeData();
});//t=Math.random();
})
$("#load-user-id").click(function(){
$(".container-fluid").load("user/listUI.do",function(){
$(".container-fluid").removeData();
});//t=Math.random();
})
$("#load-menu-id").click(function(){
$(".container-fluid").load("menu/listUI.do",function(){
$(".container-fluid").removeData();
});//t=Math.random();
})
})
click()里面有一个匿名函数,是点击事件需要执行的方法,里面给.container-fluid绑定了一个load()函数,这是异步加载函数,或去找controller,得到返回的页面呈现在.container-fluid上,这个load()函数里面的function()也是一个匿名函数,这是回调函数,指的是异步加载页面之后,需要干的事.这里这个函数的作用是清除数据(可能会出现中途跳转,所以每一次这种大的菜单页面的跳转都得清除.container-fluid上绑定的数据)
心得2:角色页面加载数据:(分页的实现)
function doGetObjects(){
//获取参数信息
var pageCurrent=
$(".pagination").data("pageCurrent");
if(!pageCurrent)pageCurrent=1;
var params={"pageCurrent":pageCurrent}
var name=$("#searchNameId").val();
if(name)params.name=name;
//发送异步请求
$.ajax({
url:"role/doFindPageObjects.do",
dataType:"json",
data:params,
success:function(result){
if(result.state==1){
setTableBodyRows(result.data.records);
setPagination(result.data)
}else{
alert(result.message);
}
}
});
}
这个函数时去异步加载角色数据,分页调用的也是这个函数,page.html里面js代码的实现是通用的,所以方法名得跟分页调用的方法名一致,这个函数时要先获取当期页(绑定在”.pagination”的data()里面),这个时page.html里面的,因为page.html是加载在role_list.html这个页面中的,所有分页的任何信息都可以获取到,如果当前页为空的话,说明是第一次加载,给一个默认值1,去加载角色信息,只需要传pageCurrent,就行.返回的是一个JsonReslut里面封装了一些状态,或者异常的信息,只要是跟想页面输出数据,在服务端都得返回这个JsonResult对象.
JsonResult是一个值对象(在common.vo包里面)
/**通过此对象对控制层数据进行封装
* 1)正常数据
* 2)异常数据
* */
public class JsonResult {
private static final int SUCCESS=1;
private static final int ERROR=0;
/**状态码*/
private int state=SUCCESS;
/**状态信息*/
private String message;
/**具体数据*/
private Object data;
public JsonResult() {
message="Action OK";
}
public JsonResult(Object data){
this.data=data;
}
public JsonResult(Throwable exp){
this.state=ERROR;
this.message=exp.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创建返回给页面的,构造方法可以根据需要多定义几个,里面有默认的属性值,因为默认返回的是正确的信息,所有状态码是1
对于角色信息的呈现,需要将所有的信息显示在页面上,所以就得包含每一条角色数据,以及当前页,总页数,总记录数,这也是一个值对象PageObject对象,这个值对象,是由service层封装之后返回给controller层的,
/***
* VO: 封装当前页数据(记录信息和分页信息)
*
* @author adminitartor
* @param <T>
*/
public class PageObject<T> {
/** 每页显示的记录 */
private List<T> records;
/** 总页数 */
private int pageCount;
/** 总记录数 */
private int rowCount;
/** 当前页面大小 */
private int pageSize = 3;
/** 当前页的页码 */
private int pageCurrent = 1;
public List<T> getRecords() {
return records;
}
public void setRecords(List<T> records) {
this.records = records;
}
public int getPageCount() {
pageCount = rowCount / pageSize;
if (rowCount % pageSize != 0) {
pageCount++;
}
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getRowCount() {
return rowCount;
}
public void setRowCount(int rowCount) {
this.rowCount = rowCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageCurrent() {
return pageCurrent;
}
public void setPageCurrent(int pageCurrent) {
this.pageCurrent = pageCurrent;
}
}
里面的getPageCount()是需要计算的,不用在service层计算,只需要把总记录数,pageSize()传给这个PageObject对象就行了,这个PageObject被封装在JsonResult里面,页面获取总页数时,会调用getPageCount()的方法,这样就直接在PageObject对象的getPageCount()方法进行计算,因为计算的方法都是一样的
心得3:数据的呈现:
function setTableBodyRows(data){
//1.获取body对象
var tBody=$("#tbodyId");
tBody.empty();
//2.迭代data,将data添加到Body中
for(var i in data){
var tr=$("<tr></tr>");
//tr对象上绑定一个id值(唯一标识这行记录)
tr.data("id",data[i].id);
var tds="<td><input type='checkbox' name='checkItem' value='"+data[i].id+"'></td>"+
"<td>"+data[i].name+"</td>"+
"<td>"+data[i].createdTime+"</td>"+
"<td>"+data[i].modifiedTime+"</td>"+
"<td><button type='button' class='btn btn-default btn-update'>update</button></td>";
tr.append(tds);
tBody.append(tr);
}
//每个checkbox上注册一个click事件
$("tbody input[name='checkItem']").click(doChangeCheckAllState);
}
这是通过追加的方式来进行数据的呈现,这个函数走到最后,所有的行数据都有了,就得给每一个多选框绑定点击事件了(这也是需要注意的一点:在使用到某个元素,数据,一定要看它是什么时候加载到页面的,)如果使用前还没有加载到页面,那么是没有效果的.
这里有两个小实现:点击全选的时候,下面的多选框都跟着变;当所有的多选框都选中或都未选中时,全选框也要跟着变:
//执行全选
$("#checkAll").change(function(){
doCheckAll($(this).prop("checked"));
});
执行全选函数的调用是在$(function())页面加载函数里面,因为,全选框是在页面上存在的,不需要去查数据库,把全选框的是否选中数据通过参数传递给
doCheckAll()方法,
---------------------------------------------------------------------------------------------------------------------
//执行全选操作(让列表中的checkbox的状态与表头中checkbox状态一致)
function doCheckAll(state) {//dom
/*$("tbody input[name='checkItem']")
.each(function(){
$(this).prop("checked",state)
}); */
$("tbody input[name='checkItem']").prop("checked", state);
}
让所有的checkbox的属性跟着全选框一起改变,有两种实现,这两种实现都可以,第二种直用prop,没有调用each()函数,这也是可以实现的.
心得4:分页的实现
分页的实现是通用的,这个项目的每个页面都要用到分页:
<script type="text/javascript">
$(".pagination")
.on("click",".first,.pre,.next,.last",doJumpToPage)
//设置分页
function setPagination(data){
//1.初始化总页数
$(".pageCount").html("总页数("+data.pageCount+")");
//2.初始化总记录数
$(".rowCount").html("总记录数("+data.rowCount+")");
//3.初始化当前页页码
$(".pageCurrent").html("当前页("+data.pageCurrent+")");
//4.绑定数据
$(".pagination").data("pageCurrent",data.pageCurrent);
$(".pagination").data("pageCount",data.pageCount);
}
function doJumpToPage(){
//1.获取当前页码以及总页数
var pageCurrent=
$(".pagination").data("pageCurrent");
var pageCount=
$(".pagination").data("pageCount");
//2.根据当前点击的对象执行pageCurrent加减操作
var cls=$(this).prop("class");
if(cls=="pre"&&pageCurrent>1){
pageCurrent--;
}else if(cls=="next"&&pageCurrent<pageCount){
pageCurrent++;
}else if(cls=="first"){
pageCurrent=1;
}else if(cls=="last"){
pageCurrent=pageCount;
}
//3.保存当前pageCurrent的值
$(".pagination").data("pageCurrent",pageCurrent);
//4.按指定页码重新执行查询
doGetObjects();
}
</script>
分页页面是要在role_list.html
页面加载都已经加载上了,设置分页条上的数据是在一查询到数据之后就立刻调用,是在role_list.html
里的getObject
的ajax函数里面调用的,
这个分页需要注意的,绑定点击事件的这种鞋发,已经获得当前这个类的类名,进行判断是上一个,还是下一页,还有需要注意的是,点击完之后,还得把pageCurrent
作为数据保存在.pagination
里面,这个一定要保存,页面pageCurrent
在这里会发送改变
具体的一些细节我也写了笔记(前端需要注意的这一跨块)