BOS3

1、用户登录认证自定义拦截器编码  

  用户登录的校验,通过拦截器来进行过滤

  这里肯定是通过Struts2的拦截器进行功能的实现了。intecepter

  MethodFilterIntercepter拦截器增强:可以对action里面的业务方法进行拦截设置。

  MethodFilterIntercepter有两个属性

protected Set<String> excludeMethods = Collections.emptySet();
protected Set<String> includeMethods = Collections.emptySet();

  excludeMethods - 要从拦截器处理中排除的方法名称

  includeMethods - 要包含在拦截器处理中的方法名称

  如果方法名称在includeMethods和excludeMethods中都可用,则它将被视为包含的方法:includeMethods优先于excludeMethods

  那么现在要做的就是进行loginInteceptor拦截器的编写,以及对login方法的放行。

(1)LoginInteceptor类(拦截器,判断是否登录。未登录的返回到登录界面。无法跳过登录直接进入系统界面)

   1、继承MethodFilterInterceptor(注意,这是一个类而不是接口)

    

@SuppressWarnings("all")
@Component("loginInterceptor")
public class LoginInteceptor extends MethodFilterInterceptor{
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        //判断用户是否登录,从session中找用户信息
        User existUser=(User)ServletActionContext.getRequest().getSession().getAttribute("exitUser");
        //未登录,返回未登录错误码
        if(existUser==null) {
            return "no_login";
        }else {
            //如果已经登录了,调用下一个组件。通过invocation去调用invoke方法。
            return invocation.invoke();
        }
    }
}

  如果session中有用户信息就证明以及登录过了。调用下一个组件,通过invocation的invoke方法调用。

  拦截器由Struts2的前端控制器来创建,交由spring进行管理。这里拦截器设置成单例就可以了。

  2、在struts.xml中进行注册

    不能讲拦截器写在action标签中,这样只有走这个action的时候才会执行拦截器。

    要写在package标签中

    

    <package name="bos" extends="json-default">
        <!-- 注册拦截器 -->
        <interceptors>
            <!-- 通过伪类名来注册拦截器,因为是由spring创建的,所以使用@Component("loginInterceptor")的名字就可以了,一定要一致 
                。完成注册。放入栈中。这个拦截器对所有action都有效 -->
            <interceptor name="myLogin" class="loginInterceptor">
            </interceptor>
            <!-- 栈 加一个是默认栈,必须要加。 -->
            <interceptor-stack name="mystack">
                <!-- 自己定义拦截器方法拦截设置 -->
                <interceptor-ref name="myLogin">
                    <!-- 这里一定要写excludeMethods,执行login方法的时候不会执行拦截器操作。 -->
                    <param name="excludeMethods">login</param>
                </interceptor-ref>
                <!-- 名称必须是defaultStack :默认栈 -->
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        <!-- 拦截器作用域。注册完之后启动,拦截器的作用域。需要对所有拦截器都有效 .它对这个包里面的所有的action都有效。但是userAction也在因为设置了它的父包是bos 
            @ParentPackage("bos"),因此对它的UserAction下的方法也有效。 但是由于login方法需要走拦截器,如果login方法都需要登录的话,是永远登录不上的。 
            如果登录就会提示找不到方法,登录不上。 -->
    <!-- 不写这段代码的话就相当于拦截器没有执行,只是定义了 -->
        <default-interceptor-ref name="mystack"></default-interceptor-ref>

        <!-- 定义全局结果集视图 -->
        <global-results>
            <result name="no_login" type="redirect">/login.jsp</result>
        </global-results>
        <action name="index">
            <result>/index.jsp</result>
        </action>
        <!-- 需要进行权限控制的页面访问 -->
        <action name="page_*_*">
            <result type="dispatcher">/WEB-INF/pages/{1}/{2}.jsp</result>
        </action>
    </package>

  

  package
Content Model : (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, default-
class-ref?, global-results?, global-exception-mappings?, action*)

  按照这个来编写顺序。

  首先进行来loginInceptor注册。这里要通过伪类名进行注册,因为是有spring进行创建的。@Component("loginInterceptor")

  

<interceptor name="myLogin" class="loginInterceptor"></interceptor>

  所以这里的class填写@Component的名称就可以了。

  拦截器栈:

  

            <!-- 栈 加一个是默认栈,必须要加。 -->
            <interceptor-stack name="mystack">
                <!-- 自己定义拦截器方法拦截设置 -->
                <interceptor-ref name="myLogin">
                    <!-- 这里一定要写excludeMethods,执行login方法的时候不会执行拦截器操作。 -->
                    <param name="excludeMethods">login</param>
                </interceptor-ref>
                <!-- 名称必须是defaultStack :默认栈 -->
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>

  在栈内添加自定义的拦截器,并设置这个拦截器对login方法放行。这里的name一定要完全写excludeMethods,MethodFilterInteceptor的属性名称。

  然后本地栈一定要进行添加。

     <!-- 名称必须是defaultStack :默认栈 -->
 <interceptor-ref name="defaultStack"></interceptor-ref>

  这样是完成了拦截器的注册,但是需要进行拦截器作用域的设置,否则该拦截器是不生效的。它对这个bos包中的所有action都有效。由于UserAction因为设置了父包为bos。

@ParentPackage("bos")

  因此它对UserAction下的方法也有效。所以UserAction的login方法需要走拦截器,但是如果login方法也要走loginInteceptor拦截器的话,那它永远是登录不上的。所以要添加

在这个拦截器中添加excludeMethods

  拦截器作用域,这句话是所有action自动调用的拦截器栈。(个人理解为将自己设置的栈添加到默认拦截器栈中,所有页面都要走这个拦截器)

<default-interceptor-ref name="mystack"></default-interceptor-ref>

  在未检测到session中有用户信息的时候。返回no_login.定义它的全局结果集。

  

        <!-- 定义全局结果集视图 -->
        <global-results>
            <result name="no_login" type="redirect">/login.jsp</result>
        </global-results>

  及:未通过登录直接跳转到/login.jsp页面。

  

2、在页面显示用户的用户名和IP

  

[<strong>超级管理员</strong>],欢迎你!您使用[<strong>192.168.1.100</strong>]IP登录!
[<strong>超级管理员</strong>],欢迎你${sessionScope.existUser.username}!
您使用[<strong>${pageContext.request.remoteAddr}</strong>]IP登录!

  显示从session中获取的数据。

3、修改密码的正则校验

    

  首先一个弹窗:EasyUI提供了这种功能:
  窗体里显示用户的一些信息
  在点击修改密码的时候弹出一个窗口。

  

        function editPassword() {
        $('#editPwdWindow').window('open');
    }
    添加点击事件
            <div class="easyui-layout" fit="true">
            <div region="center" border="false" style="padding: 10px; background: #fff; border: 1px solid #ccc;">
                <table cellpadding=3>
                    <tr>
                        <td>新密码:</td>
                        <td><input id="txtNewPass" type="Password" class="txt01" /></td>
                    </tr>
                    <tr>
                        <td>确认密码:</td>
                        <td><input id="txtRePass" type="Password" class="txt01" /></td>
                    </tr>
                </table>
            </div>
            <div region="south" border="false" style="text-align: right; height: 30px; line-height: 30px;">
                <a id="btnEp" class="easyui-linkbutton" icon="icon-ok" href="javascript:void(0)" >确定</a> 
                <a id="btnCancel" class="easyui-linkbutton" icon="icon-cancel" href="javascript:void(0)">取消</a>
            </div>

            其中确定的ID   id="btnEp"

修改密码需要定义一些东西:比如,不能不输入,两次输入要一致,密码长度限制
这些限制和校验都是在确定里面做的。
确定添加点击事件。

  

    $("#btnEp").click(function(){
            //修改密码的实现方法
            //1、获取新密码,判断这个密码是否有效    JS的校验     (是否为空。特殊字符,长度)
            var newPwd=$("#txtNewPass").val();
            if(newPwd==""||newPwd==null){
                $.messager.alert("警告","新密码必须填写","warning");
                return;
            }
            //空白字符
            var reg=/s+/;  //+代表一个。这里是有一个或多个空白字符就会被检测到
            if(reg.test(newPwd)){
                $.messager.alert("警告","密码不能包含空格","warning");
                return;
            }
            //定义长度,不在这个长度范围的就会提示
            if(newPwd.length<=0||newPwd.length>=7){
                $.messager.alert("警告","密码为1-6位","warning");
                return;
            }
            //2、重复密码判断    两次密码要求一致  新旧密码不一致就会提示
            if(newPwd!=$("#txtRePass").val()){
                $.messager.alert("警告","密码不一致","warning");
                return;
            }
            //3、密码一致   有效   发送ajax请求给action --->service--->Dao  数据库    当前用户密码的update操作
            //4、关闭窗口
        });

4、当前action的父包是bos,而bos继承“json-default” ,所以可以用json格式

 editPassword方法

    @Action(value = "userAction_editPassword",results= {@Result(name="editPassword",type="json")})
    public String editPassword() {
        try {
        User existUser=(User)getSessionAttribute("existUser");
        serviceFacade.getUserService().editPassword(model.getPassword(),existUser.getId());
        push(true);//压栈
        }catch (Exception e) {
            e.printStackTrace();
            push(false); //给到页面
        }
        return "editPassword";
    }
    //修改密码  spring  data  jpa
    @Modifying
    @Query("update User set password=?1 where id=?2") //只有这个不能修改,因为这个是查找,必须加@Modifying才可以修改
    public void editPassword(String password, String id);

这里必须添加 @Modifying注解,因为@Query是只限于查询。

  涉及到增删改的都要添加事务。在配置文件中添加jps事务管理。

  

       <!-- spring  data jpa 事务管理 配置 -->
      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
//打开注解扫码
<tx:annotation-driven transaction-manager="transactionManager"/>

 5、json-plugin插件原理说明

  

    @Action(value = "userAction_editPassword",results= {@Result(name="editPassword",type="json")})
    public String editPassword() {
        try {
        User existUser=(User)getSessionAttribute("existUser");
        serviceFacade.getUserService().editPassword(model.getPassword(),existUser.getId());
        push(true);
        }catch (Exception e) {
            e.printStackTrace();
            push(false); //给到页面
        }
        return "editPassword";
    }

  上面的代码是修改了密码后将结果集压栈。所以在栈顶的就是true/fasle。

  结果集使用的是json-default,

  action的父包是bos,而bos又是json-default的子包。

<package name="bos" extends="json-default">

所以type=json的底层就是走下面的这个结果集

  

        <result-types>
            <result-type name="json" class="org.apache.struts2.json.JSONResult"/>
        </result-types>

  底层是通过response返回ajax请求,数据来自createJSONString(HttpServletRequest,Object);

  

会判断是否有用户自定义配置,如果有用户自定义配置就找到名称为root的值,obj。如果没有就去值栈中获取栈顶的元素。

6、 取派员表和实体类生成

  利用power designer生成数据表,然后通过反向生成实体类来建立表。

  先配置reveng.xml文件。

reveng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>
    <!-- 以下标签是Oracle使用的 -->
    <!-- <schema-selection match-table="T_USER" match-schema="SH1208"/> -->
    <!-- <schema-selection match-table="BC_.*" match-schema="SH1208"/> -->
    <!-- <table name="BC_DECIDEDZONE" schema="SH1208" -->
    <!-- class="com.zero.bos.domain.bc.DecidedZone"> -->
    <!-- <primary-key> -->
    <!-- <generator class="uuid"></generator> -->
    <!-- </primary-key> -->
    <!-- </table> -->
    <!-- match-table:访问表的 (代表这个表能够被找到).对于数据库zero_bos下面t_user表定义实体类生成规则 -->
    <!-- 可以被扫码到 -->
    <schema-selection match-table="t_user" match-catalog="zero_bos" />
    <!--.代表任意字符,*代表0或多个 -->
    <schema-selection match-table="bc_.*" match-catalog="zero_bos" />
    <!-- 实体类生成规则定义, -->
    <table name="bc_staff" catalog="zero_bos" class="com.zero.bos.domain.bc.Staff">
        <primary-key>
            <!-- uuid主键(字符) identity:主键自增长 -->
            <generator class="uuid"></generator>
        </primary-key>
    </table>

    <table name="bc_decidedzone" catalog="zero_bos" class="com.zero.bos.domain.bc.DecidedZone">
        <primary-key>
            <!-- uuid主键(字符) identity:主键自增长 -->
            <generator class="uuid"></generator>
        </primary-key>
    </table>

    <table name="bc_region" catalog="zero_bos" class="com.zero.bos.domain.bc.Region">
        <primary-key>
            <!-- uuid主键(字符) identity:主键自增长 -->
            <generator class="uuid"></generator>
        </primary-key>
    </table>

    <table name="bc_subarea" catalog="zero_bos" class="com.zero.bos.domain.bc.Subarea">
        <primary-key>
            <!-- uuid主键(字符) identity:主键自增长 -->
            <generator class="uuid"></generator>
        </primary-key>
    </table>
    <!-- 实体类生成规则定义, -->
    <table name="t_user" catalog="zero_bos" class="com.zero.bos.domain.user.User">
        <primary-key>
            <!-- uuid主键(字符) identity:主键自增长 -->
            <generator class="uuid"></generator>
        </primary-key>
    </table>
</hibernate-reverse-engineering>

  然后maven执行:hibernate3:hbm2java

  建模完成。

7、取派员的增加

  点击保存按钮,实现取派员信息的添加。

  

1         <div region="north" style="height:31px;overflow:hidden;" split="false" border="false" >
2             <div class="datagrid-toolbar">
3                 <a id="save" icon="icon-save" href="#" class="easyui-linkbutton" plain="true" >保存</a>
4             </div>
5         </div>
6         
7         <div region="center" style="overflow:auto;padding:5px;" border="false">
8             <form id="addStaffForm" action="${pageContext.request.contextPath }/bc/staffAction_save" method="post">
9                 <table class="table-edit" width="80%" align="center">

对click添加点击事件

 

编写action代码

  

    // 取派员添加
    @Action(value = "staffAction_save", results = {
            @Result(name = "save", location = "/WEB-INF/pages/base/staff.jsp") })
    public String save() {
        try {
            serviceFacade.getStaffService().save(model);
        } catch (Exception e) {
            e.printStackTrace();
            push(false);
        }
        return "save";
    }
转发是可以到web-inf目录下的,重定向不行。

父类中注入门面类

  

// 父类中 注入 门面业务层
@Autowired
protected FacadeService serviceFacade;

  DAO继承

public interface StaffDao extends JpaRepository<Staff, String> {
    
}

完成

  

JavaEE 开发 
1: 客户端 页面 js 编写 (easyui +jquery)

2:服务器端:  接受请求数据?
Action( 类名必须以Action 结尾  包名一定要包含  action|actions|struts|struts2  @Controller  @Scope @Namespace @ParentPackage)  
-service  (注解开发  接口+实现类(@Service  @Transaction)
dao  接口 继承 JPARepostitory
)

3:结果集跳转 (转发跳转 WEB-INF   查询: ajax)

8、取派员的分页查询和显示

  (1)首先页面使用的假数据格式是这样的

{                                                      
    "total":100,    
    "rows":[ 
        {"id":"001","name":"李大","telephone":"13912345678","haspda":"1","deltag":"0","standard":"10-20公斤","station":"杭州分部"},
        {"id":"002","name":"李二","telephone":"13912345678","haspda":"1","deltag":"0","standard":"10-20公斤","station":"杭州分部"}
    ]
}

  所以我们后台返回的数据也要是这种格式的才可以正常显示。

  (2)easyui对分页查询的实现

    从服务器端拿json格式数据。上面的数据就是easyui框架需要的分页数据

    要求服务器端是数据格式要有:(1)total:取派员总记录数
                                          (2)rows:每页记录数  [{},{}] 要求是数组    

    然后ajax json数据回送    easyui插件自动实现分页

    客户端:进行分页查询的时候,客户端必须提供   请求页码和记录数

    在点击下一页的时候就会向url发送请求,并且带参数

    page和rows,这个插件在进行分页查询 时候,它会自动的向我们的datagrid的url地址发送最新的页码和每页记录数

    然后后台代码会进行数据库查询再返回数据

    

  分页查询的结论:客户端  url:实际的服务器请求地址

  

url : "${pageContext.request.contextPath}/bc/staffAction_pageQuery",

服务器端:BaseAction接收page和rows即可。

staffAction---->service--->dao-->List<Staff>

        但是List<Staff>序列号会变成
        [{},{},{}]   缺少total

        所以服务器端要对其进行扩展
        这种数据格式最典型的就是map
        所以要把数据往map里放
        map.putTotal
        map.putData
        栈顶--->json-plugin
        自动map--->json    字符串

功能实现:

    (1)在BaseAction中添加  

    // 分页操作 接受页面 和 每页显示记录
    protected int page;// 页码
    protected int rows;// 每页显示记录数
    //struts2属性注入 将请求数据  自动注入

    public void setPage(int page) {
        this.page = page;
    }

    public void setRows(int rows) {
        this.rows = rows;
    }

(2)将page和rows传递给StaffAction  

  

    // 取派员分页查询
    @Action(value = "staffAction_pageQuery", results = {
            @Result(name = "pageQuery", type="json") })
    public String pageQuery() {
        Map<String,Object> data=new HashMap<String,Object>();
        try {
            PageRequest pageable=new PageRequest(page-1, rows);
            Page <Staff> pageData=serviceFacade.getStaffService().pageQuery(pageable);
            //dao  参数   Pageable pageable   自动完成分页查询   将分页结果数据   自动封装到Page<T>,所以这里不能是List<Staff>,否则无法封装
            
            //从Page  对象获取总记录数  和   每页分页记录数List<Staff>
            data.put("total", pageData.getTotalElements());
            data.put("rows", pageData.getContent());
            push(data);
        } catch (Exception e) {
            e.printStackTrace();
            push(false);
        }
        return "pageQuery";
    }

  底层两个参数page和rows走的是dao层。

  StaffDao是继承JpaRepository<Staff, String>

  而JpaRepository接口继承PagingAndSortingRepository
        @NoRepositoryBean
        public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {

----------
        @NoRepositoryBean
        public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

----------
        @NoRepositoryBean
        public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

        这里就可以看到PagingAndSortingRepository做的分页。所以要实现分页功能,要按它的要求提供相关的参数。
        `@NoRepositoryBean
        public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

    /**
     * Returns all entities sorted by the given options.
     * 
     * @param sort
     * @return all entities sorted by the given options
     */
    Iterable<T> findAll(Sort sort);

    /**
     * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
     * 
     * @param pageable
     * @return a page of entities
     */
    Page<T> findAll(Pageable pageable);
}`
    
----------

    需要 Pageable,给它这个,它就会自动完成分页。结果数据
    `Page <Staff> pageData=serviceFacade.getStaffService().pageQuery(page,rows);`
    这里的Pageable也是一个接口。所以要看它有哪些实现类和方法。
    它只有一个PageRequest实现类。

  为spring  jpa服务,要讲页码和每页记录数封装到Pageable

      easyui要的map格式,所以要把page对象放到map中,这样就可以序列化成想要的样式。
    在么有配root的情况下,easyui从栈顶拿序列化的数据。
    为什么不能直接序列化pageData而是要先放入map再进行序列化。
    
    如果此时添加取派员,数据库会增加,但是前端页面并不会显示添加的信息。
    同时后台会报错,延迟加载错误。

      Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zero.bos.domain.bc.Staff.decidedZones, no session or session was closed

  典型的延迟加载的错误。

问题原因:

      前面每页10条的时候不需要分页,每页去查下一页。现在更改为每页1条时,就需要查下一页。会用到limit
      每页立刻去发送语句查询。
      原理:事务管理    ---service层
         事务结束   session就没有了
         延迟数据(entitymanager)在的一级缓存区里。
  没有分页查询的语句,只有记录总数的。

      对staff的this.decidedZones = decidedZones;是延迟的,不用的时候不加载。

  


    StaffAction   Page<Staff> ----- >延迟的定区数据set
    action  没有延迟数据只有Staff数据
    会去找是
    不掉get方法就不会序列号

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "staff")
    @JSON(serialize=false)
    public Set<DecidedZone> getDecidedZones() {
        return this.decidedZones;
    }


 问题2:
    但是这个只有只有一页,而且显示的数据并不是第一条的数据。
    查询是从第二页查的,而不是第一页。
    问题出在action中。
    在PageRequest源码看就会知道,它的页面是从0开始的。第一页的下标是0,所以这里我们让它-1就可以了。
    但是总页面还是不对。那么就是数据格式显示的问题,total数据没有传递过来。
    问题3:当我们点击取派员的时候,格式显示的不正确,

  原因是jps默认的起始页是0.所以page要-1.
    

      Page<Staff>  没有有延迟的那个数据,只有satff的数据,没有延迟的数据。
    数据放入值栈,    json-plugin序列号数据
    调用Staff实体类所有的getter  获取数据才能json字符生成
    但是集合 延迟数据已经没有了,没有session了。就会报错。
    @JSON 插件从值栈中获取数据  进行json字符串生成的时候  调用目标对象get方法! 
    Serialize=false 插件不会调用 get 方法获取数据

  Page<T> findAll=   ;一行就实现了分页。

 9、小结:

    hibernate默认是lazy,懒加载。没有向数据库发数据,但是缓存有。

    有这些数据都是基于事务的,一旦事务结束。数据就没有了。

    或者可以把事务放到web层,放大。web层一直存在。

    

  

  

坚持就是胜利
原文地址:https://www.cnblogs.com/xiaotieblog/p/8722718.html