SSH综合练习-第1天

SSH综合练习-仓库管理系统-第一天

 

综合练习的整体目的

整合应用 Struts2 、Hibernate、Spring 、Mysql 、 jQuery Ajax、java基础知识

  1. 熟悉企业SSH 基础架构
  2. 掌握在SSH基础架构 进行CRUD 编写(抽取代码) (单表、多表 )
  3. JQuery Ajax 编程实现(struts2整合json的插件包)

 

今天的主要内容:

  1. 开发前的准备工作(原型导入、需求功能分析)
  2. 开发基础环境的搭建(SSH环境搭建、MyEclipse逆向生成实体类和映射)
  3. 登录功能的实现(三层架构搭建、通用代码抽取、基于泛型的通用DAO
  4. 仓库管理功能的实现(单表的CRUD、值栈的使用、其他代码抽取)
  5. 货物管理功能的实现-入库(仓库列表的AJAX请求Spring和Ehcache缓存集成

 

  1. 开发前的准备工作

    1. 原型(静态页面)导入

原型就是软件的一个早期可运行的版本,它实现了目标系统的某些功能,主要用于和用户确定其明确的需求。以及开发编码的基础页面。

原型:低精度+高精度

  • 低精度:原型页面粗糙,只是功能演示,不能作为开发使用。功能参考。
  • 高精度:原型页面精美,达到将来产品的效果。可以作为开发使用。

我们这里提供的原型就是仓库管理系统静态页面的工程。

【思考】

如何导入原型工程?

  • 开发工具自动导入
  • 手动分类导入-推荐

 

新建Web项目storemanager:

 

导入静态页面和源码配置等:

页面赋值到WebRoot,源码和配置复制到src

【提示】

复制的时候,不要覆盖WEB-INF和META-INF目录。

 

【部署测试】

将工程部署到tomcat中,启动服务器,查看是否正常能访问。

 

  1. 需求功能分析

详细功能可以参考需求文档,也可以查看原型来了解。

要开发的功能如下:

  • 登录功能 (搭建项目基础架构 ) ---- userinfo用户表
  • 仓库管理 (仓库信息增删改查) ----- store仓库表
  • 货物进出库 (货物的存入仓库、 离开仓库 ) ---- goods货物表
  • 库存管理 (查看仓库中货物情况-盘点 )
  • 历史记录查询 (将货物进出库进行记录操作 )---- history 历史记录表

数据表:

表之间的关系:

表之间的关系:

用户表:userinfo表,独立

表名(中文)

表名(英文)

 

用户表

userinfo

 

序号

字段名

类型

长度

NULL

说明

1

id

varchar

32

no

主键ID    

2

name

varchar

50

yes

用户名

3

password

varchar

32

yes

密码

 

仓库表:store

1----- * 货物goods (一个仓库 可以存放多种货物 )

表名(中文)

表名(英文)

 

仓库表

store

 

序号

字段名

类型

长度

NULL

说明

1

id

varchar

32

no

主键ID    

2

name

varchar

32

yes

仓库名称

3

addr

varchar

100

yes

仓库地址

4

Manager

varchar

32

yes

仓库管理员

 

货物表:goods

1 ---- * 历史记录history (一种货物,可能出入库多次,每次操作都要记录历史 )

表名(中文)

表名(英文)

 

货物表

goods

 

序号

字段名

类型

长度

NULL

说明

1

id

varchar

32

no

主键ID    

2

name

varchar

50

yes

货物名称

3

nm

varchar

10

yes

简记码

4

unit

varchar

10

yes

计量单位

5

amount

double

 

yes

出/入库数量

6

storeid

varchar

32

yes

仓库id,对应仓库表主键

 

历史记录表:history

表名(中文)

表名(英文)

 

历史记录表

history

 

序号

字段名

类型

长度

NULL

说明

1

id

varchar

32

no

主键ID    

2

goodsid

varchar

32

yes

货物id,对应货物表主键

2

datetime

varchar

19

yes

操作时间(当前时间)

3

_type

varchar

1

yes

出/入库标识,1入库,2出库

4

amount

double

 

yes

本次出/入库数量

5

remain

double

 

yes

余量

6

_user

varchar

50

yes

操作人(Session中获取),直接保存名称,不引用userinfo表

 

 

 

  1. 数据库生成

 

Mysql:

使用一个用户,建一个数据库,将脚本在该数据库上运行,建立相应的表。

 

步骤:

 

  1. 新建用户store

在Add User页面中,用户名和密码都是store

  1. 新建数据库itcaststore

  1. 给store用户赋予itcaststore数据库的相应的权限(所有权限)

  2. 重新使用store用户登录

登录之后:

 

  1. 选中数据库itcaststore,将sql\mysql\store.sql脚本粘贴过来,执行:创建4张表。

     

     

    create table userinfo( /*用户表*/

    id varchar(32) primary key,

    name varchar(50),/*登录名*/

    password varchar(32)/*密码*/

    );

    /*仓库表*/

    create table store(

        id varchar(32) primary key,

        name varchar(32),/*仓库名称*/

        addr varchar(100),/*仓库所在地*/

        manager varchar(32) /*仓库管理人员,不关联userinfo表*/

    );

     

    /*货物表*/

    create table goods(

        id varchar(32) primary key,

        name varchar(50),/*货物名称*/

        nm varchar(10),/*货物简记内码,如阿斯匹林为ASPL*/

        unit varchar(10), /*计量单位,1:个,2:GK,3:只,..*/

        amount numeric(10,2),/*库存数量*/

        storeid varchar(32),/*所在仓库ID*/

        constraint foreign key(storeid) references store(id)

    );

    /*出入库历史记录*/

    create table history(

        id varchar(32) primary key,

        goodsid varchar(32),/*货物ID*/

        datetime varchar(19),/*出入库时间*/

        _type char(1),/*类型1:入库,2:出库*/

        amount numeric(10,2),/*这次出入库的数量*/

        remain numeric(10,2),/*余量*/

        _user varchar(50), /*操作员名称,直接保存名称,不引用userinfo表*/

        constraint foreign key(goodsid) references goods(id)

    );

 

  1. 开发基础环境的搭建(SSH环境搭建、MyEclipse逆向生成实体类和映射)

技术选型:要用什么技术组件(ssh+mysql)

  1. SSH开发环境构建

方式:引入ssh的jar和相关配置(web.xml和一些核心配置文件)

参考:需要的材料:课前资料中的

 

第一步:导入jar包。

 

第二步:导入web.xml(struts的过滤器和spring的监听器):

可以直接覆盖WEB-INF

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5"

    xmlns="http://java.sun.com/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name></display-name>

    <!-- Spring ServletContextListener -->

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

 

    <!-- 加载配置文件默认 WEB-INF/applicationContext.xml -->

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>classpath:applicationContext.xml</param-value>

    </context-param>

 

    <!-- Struts2 核心 过滤器 -->

    <filter>

        <filter-name>struts2</filter-name>

        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>struts2</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

 

    <welcome-file-list>

        <welcome-file>index.jsp</welcome-file>

    </welcome-file-list>

</web-app>

 

第三步:导入配置文件:

通常实际项目开发时 : "项目源码、测试代码、配置文件" 一般是分开的,所以我们首先来规划一下源码文件夹。

新建源码文件夹:

命名为resource(用来存放资源文件)

命名为test(用来存放测试文件)

我们可以新建不同功能的源代码文件夹:

将配置文件都复制到resources。

 

最终结果:

其中:

  1. db.properties文件:

    jdbc.driverClass = com.mysql.jdbc.Driver

    jdbc.url = jdbc:mysql:///itcaststore

    jdbc.username = store

    jdbc.password = store

  2. log4j.properties文件

    ### direct log messages to stdout ###

    log4j.appender.stdout=org.apache.log4j.ConsoleAppender

    log4j.appender.stdout.Target=System.out

    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

    log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

     

    ### direct messages to file mylog.log ###

    log4j.appender.file=org.apache.log4j.FileAppender

    log4j.appender.file.File=c:/mylog.log

    log4j.appender.file.layout=org.apache.log4j.PatternLayout

    log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

     

    ### set log levels - for more verbose logging change 'info' to 'debug' ###

     

    log4j.rootLogger=info, stdout

     

  3. messages.properties

    国际化资源文件:用来实现项目的国际化

  4. struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE struts PUBLIC

        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

        "http://struts.apache.org/dtds/struts-2.3.dtd">

     

    <struts>

        <!--

            开发者模式:关闭:副作用:如果是ajax请求的情况下,一旦出错,不会在页面上打印错误

            所以采用struts.i18n.reload和struts.configuration.xml.reload的模式

        -->

    <!-- <constant name="struts.devMode" value="true" /> -->

    <!-- 不用重启服务器 -->

        <constant name="struts.i18n.reload" value="true" />

        <constant name="struts.configuration.xml.reload" value="true" />

        <!-- 表单样式 -->

        <constant name="struts.ui.theme" value="simple" />

        <!-- 加载src下的国际化文件messages.properties -->

        <constant name="struts.custom.i18n.resources" value="messages" />

     

    <package name="default" namespace="/" extends="struts-default">

     

    </package>

     

    </struts>

    (5)applicationContext.xml文件

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:cache="http://www.springframework.org/schema/cache"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop.xsd

    http://www.springframework.org/schema/tx

    http://www.springframework.org/schema/tx/spring-tx.xsd

    http://www.springframework.org/schema/cache

    http://www.springframework.org/schema/cache/spring-cache.xsd">

        <!-- 引入外部属性文件 -->

        <context:property-placeholder location="classpath:db.properties" />

     

        <!-- 连接池 -->

        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

            <property name="driverClass" value="${jdbc.driverClass}" />

            <property name="jdbcUrl" value="${jdbc.url}" />

            <property name="user" value="${jdbc.username}" />

            <property name="password" value="${jdbc.password}" />

        </bean>

     

        <!-- sessionFactory -->

        <bean id="sessionFactory"

            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

            <!-- 第一部分: 连接池 -->

            <property name="dataSource" ref="dataSource" />

            <!-- 第二部分: hibernate常用属性 -->

            <property name="hibernateProperties">

                <props>

                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>

                    <prop key="hibernate.hbm2ddl.auto">update</prop>

                    <prop key="hibernate.show_sql">true</prop>

                    <prop key="hibernate.format_sql">true</prop>

                </props>

            </property>

            <!-- 第三部分: 引入hbm -->

            <!-- <property name="mappingResources">

                <list>

                    <value>cn/itcast/storemanager/domain/Goods.hbm.xml</value>

                    <value>cn/itcast/storemanager/domain/History.hbm.xml</value>

                    <value>cn/itcast/storemanager/domain/Store.hbm.xml</value>

                    <value>cn/itcast/storemanager/domain/Userinfo.hbm.xml</value>

                </list>

            </property> -->

            <!-- 可以批量引入hbm

            支持通配符

             -->

            <property name="mappingLocations">

                <list>

                    <value>classpath:cn/itcast/storemanager/domain/*.hbm.xml</value>

                </list>

            </property>

        </bean>

        

        <!-- 配置事务 -->

        <!-- 配置事务管理器平台 -->

        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

            <property name="sessionFactory" ref="sessionFactory" />

        </bean>

        <!-- 事务管理通知 -->

        <tx:advice id="txAdvice" transaction-manager="transactionManager">

            <tx:attributes>

                <tx:method name="save*" read-only="false"/>

                <tx:method name="update*" read-only="false"/>

                <tx:method name="delete*" read-only="false"/>

                <tx:method name="find*" read-only="true"/>

            </tx:attributes>

        </tx:advice>

        

        <!-- 切入点和切面 -->

        <aop:config>

            <aop:pointcut expression="bean(*Service)" id="txPointcut"/>

            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

        </aop:config>

        

    </beans>

    1. 使用myeclipse反向生成功能

    不建议在原来项目上生成 ,新建web 项目abcdefg,用来保存生成的po类和hbm。

    点击:

    切换Database Explorer 视图,

    新建数据库连接

     

    选择Myeclipse视图,选择abcdefg项目,对abcdefg项目添加hibernate支持

    选择Hibernate的版本

     

    点击下一步:选择DB Driver

    选择abcdefg项目,创建包cn.itcast.storemanager.domain用来存放持久化对象和配置文件。

     

    回到数据透视图,选择表,进行反转生成

    配置选项

     

    注意:两点:

  • 包名的问题,最好实际项目用的什么包名,这里就指定什么包名。
  • xml配置的话必须选择java类的选项

 

 

修改applicationContext.xml 引入hbm映射

<!-- 第三部分: 引入hbm -->

        <property name="mappingResources">

            <list>

                <value>cn/itcast/storemanager/domain/Goods.hbm.xml</value>

                <value>cn/itcast/storemanager/domain/History.hbm.xml</value>

                <value>cn/itcast/storemanager/domain/Store.hbm.xml</value>

                <value>cn/itcast/storemanager/domain/Userinfo.hbm.xml</value>

            </list>

        </property>

最后:测试实体类是否正常:

删除数据库表,重启服务,看是否报错和自动生成表。

 

【扩展知识】批量映射配置:

<!-- 可以批量引入hbm

            支持通配符

         -->

        <property name="mappingLocations">

            <list>

                <value>classpath:cn/itcast/storemanager/domain/*.hbm.xml</value>

            </list>

        </property>

 

 

 

 

  1. 登录功能的实现(三层架构搭建、通用代码抽取、基于泛型的通用Dao)

【业务逻辑梳理】

    用户在页面输入用户和密码 -----> 数据封装model --->- 通过Service传递到Dao 查询数据库 ---> 返回Userinfo对象 ---->

如果正确,将登录信息保存到session,

如果不正确,封装ActionError 返回登录页面。

 

  1. 三层架构设计思想

 

 

建立相应的包:

  1. 修改登录页面表单

目标页面:login.jsp

使用struts2 标签,因为需要回显功能,新增struts标签的taglib:

<%@ taglib uri="/struts-tags" prefix="s" %>

修改原来的form:

<s:form name="loginForm" action="user_login" namespace="/" method="post" cssStyle="margin-top:250px;">

 

</s:form>

注意:struts2标签使用的标准的html标签,因此,属性必须加双引号。

 

修改其他标签:

用户名:<s:textfield name="name" cssClass="tx" maxLength="15" size="15" />

密码:<s:password name="password" cssClass="tx" maxLength="15" size="15"/>

增加验证信息显示标签:

<TR>

    <TD align="center" colSpan=3 width="623" height="260"

        background="<c:url value='/picture/welcome_01.gif'/>">

        <!-- 验证码返回提示 --> <br> <br> <br> <br> <br>

        <font color="#ff60a0" size="5">

            <!-- 回显错误表单信息:表单校验错误,针对具体字段 -->

            <s:fielderror></s:fielderror>

            <!-- 回显一般错误信息 -->

            <s:actionerror/>

        </font>

    </TD>

</TR>

 

验证修改的页面是否存在错误:

在浏览器中刷新页面查看。

 

  1. 编写UserAction (表现层 )

简化抽取Action类,让Action类继承BaseAction:

优势:

(1)使得所有Action都继承ActionSupport。

(2)实现模型驱动的接口全部放置到BaseAction类中完成。

(3)将Action传递值的操作封装到BaseAction中完成

 

第一步:在cn.itcast.storemanager.web.action中创建BaseAction的类:代码:

//action的父类:用来存放action的重复代码的

//通用:泛型

public abstract class BaseAction<T> extends ActionSupport implements ModelDriven<T>{

    //实例化数据模型T,模型驱动必须实例化

//    private T t = new T();

    //子类可见

    protected T model;//没有初始化

 

    public T getModel() {

        return model;

    }

    

    //在默认的构造器初始化数据模型

    public BaseAction() {

        //在子类初始化的时候,默认会调用父类的构造器

        //反射机制:获取具体的类型

        //得到带有泛型的类型,如BaseAction<Userinfo>

        Type superclass = this.getClass().getGenericSuperclass();

        //转换为参数化类型

        ParameterizedType parameterizedType = (ParameterizedType) superclass;

        //获取泛型的第一个参数的类型类,如Userinfo

        Class<T> modelClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];

        

        //实例化数据模型类型

        try {

            model = modelClass.newInstance();

        } catch (InstantiationException e) {

            e.printStackTrace();

        } catch (IllegalAccessException e) {

            e.printStackTrace();

        }

 

    }

    

    //封装值栈的操作的方法

    //root栈:栈顶map,可以通过key获取value

    protected void setToValueStackRoot(String key,Object value){

        ActionContext.getContext().getValueStack().set(key, value);

    }

    

    //root栈:栈顶对象(匿名)

    protected void pushToValueStackRoot(Object value){

        ActionContext.getContext().getValueStack().push(value);

    }

    //map栈:--推荐,可以通过key获取value

    protected void putToValueStackMap(String key,Object value){

        ActionContext.getContext().put(key, value);

    }

    

    //root栈:action的属性

    protected Object result;

    public Object getResult() {//action在root栈,因此,result也在root栈

        return result;

    }

}

 

 

第二步:创建UserAction子类代码:用来接收页面传递的user_login

//用户操作的action

//public class UserAction extends ActionSupport implements ModelDriven<Userinfo>{

public class UserAction extends BaseAction<Userinfo>{

//    //数据模型对象,在BaseAction中体现

//    private Userinfo userinfo = new Userinfo();

//    @Override

//    public Userinfo getModel() {

//        return userinfo;

//    }

    

    //注入service

    private IUserService userService;

 

    public void setUserService(IUserService userService) {

        this.userService = userService;

    }

 

    //业务方法:登录

    public String login(){

        System.out.println("用户开始登录了。。。。"+model);

        //调用业务层,查询

        Userinfo loginUser= userService.login(model);

        if(loginUser == null){

            //登录失败

            //将失败信息打印页面

            addActionError(this.getText("UserAction.loginerror"));

            //跳转到登录

            return "loginjsp";

            

        }else{

            //登录成功

            //将登录用户放入session

//            ServletActionContext.getRequest().getSession().setAttribute("loginUser", loginUser);

            ServletUtils.setLoginUserToSession(loginUser);

            //代码:可以抽取到baseAction,也可以抽取到工具类中

            //跳转到主页

            return SUCCESS;

        }

        

    }

 

}

 

简单分析:

当子类实例化的时候,会将具体的类型来代替参数化父类的T,并自动执行父类的构造方法,通过反射机制,得到具体的模型的实例。struts通过getModel得到模型对象。

开发小技巧:

  1. 可以先将代码写完再统一配置(因为配置的时候需要代码)
  2. 代码可以从前往后写(从页面-->Action-->Service-->Dao),也可以从后往前写(先写Dao,再写Action和Service)。

 

第三步:在cn.itcast.storemanager.utils包中抽取工具类ServletUtils.java:

//操作servlet相关

public class ServletUtils {

    

    //登录用户的key

    private static final String LOGIN_USER="loginUser";//常量

    

    //将登陆了用户放入session

    //满足登录和退出

    public static void setLoginUserToSession(Userinfo loginUser){

//        ServletActionContext.getRequest().getSession().setAttribute("loginUser", loginUser);

        if(null==loginUser){

            //清除登录用户

            ServletActionContext.getRequest().getSession().removeAttribute(LOGIN_USER);

        }else{

            //放登录用户

            ServletActionContext.getRequest().getSession().setAttribute(LOGIN_USER, loginUser);

        }

    }

    //从session获取登录用户信息

    public static Userinfo getLoginUserFromSession(){

//        return (Userinfo)ServletActionContext.getRequest().getSession().getAttribute("loginUser");

        Object o = ServletActionContext.getRequest().getSession().getAttribute(LOGIN_USER);

        return o == null?null:(Userinfo)o;

    }

 

}

 

 

 

 

  1. 编写UserService (业务层 )

第一步:设计新增父类Service,用于存放将来可能抽取出来的代码:

//业务层的父类:用来复用公用代码

//抽象类

public abstract class BaseService {

 

}

第二步:编写具体业务接口service

public interface IUserService {

 

    /**

     * 用户登录操作

     */

    Userinfo login(Userinfo userInfo);

 

}

第三步:编写具体业务接口的实现类UserServiceImpl.java

//继承BaseService:可以代码复用

//实现UserService:用来实现业务逻辑操作

public class UserServiceImpl extends BaseService implements IUserService {

 

    //注入dao

//    private UserDAO userDAO;

    //注入用户操作的dao

    private IGenericDao<Userinfo, String> userDao;//具体类型的dao

    public void setUserDao(IGenericDao<Userinfo, String> userDao) {

        this.userDao = userDao;

    }

 

    //登录验证,使用用户名和密码作为查询条件,查询对应的唯一对象,如果有存在则返回,不存在返回null

    public Userinfo login(Userinfo userInfo) {

        //调用dao

        //QBC

        //离线条件

        DetachedCriteria criteria =DetachedCriteria.forClass(Userinfo.class)

                .add(Restrictions.eq("name", userInfo.getName()))

                .add(Restrictions.eq("password", userInfo.getPassword()));

        List<Userinfo> list = userDao.findByCriteria(criteria);

        return list.isEmpty()?null:list.get(0);

    }

}

 

  1. 编写GenericDao 通用Dao (数据层)

 

第一步:在cn.itcast.storemanager.daol包中,通用数据接口IGenericDao.java代码:

//通用DAO接口:为了简化代码开发

public interface IGenericDao<T,ID extends Serializable> {

    

    /**

     * 保存数据

     * @param domain

     */

    public void save(Object domain);

    

    /**

     * 更新

     * @param domain

     */

    public void update(Object domain);

    

    /**

     * 删除

     * @param domain

     */

    public void delete(Object domain);

    

    /**

     * 根据id查询对象

     * @param domainClass

     * @param id

     * @return

     */

    public T findById(Class<T> domainClass, ID id);

    

    /**

     * 查询所有

     * @return

     */

    public List<T> findAll(Class<T> domainClass);

    

    //复杂条件查询

    /**

     * 命名查询

     * @param queryName

     * @param values

     * @return

     */

    public List<T> findByNamedQuery(String queryName, Object... values);

    

    /**

     * 离线条件查询

     * @param criteria

     * @return

     */

    public List<T> findByCriteria(DetachedCriteria criteria);

    

 

}

第二步:在cn.itcast.storemanager.dao.impl包中通用数据接口实现类GenericDaoImpl.java代码

//通用dao的实现

//hibernate模版类操作,继承daosupport

public class GenericDaoImpl<T,ID extends Serializable> extends HibernateDaoSupport implements IGenericDao<T, ID> {

 

    

    //保存对象

    public void save(Object domain) {

        getHibernateTemplate().save(domain);

    }

 

    //修改对象

    public void update(Object domain) {

        getHibernateTemplate().update(domain);

    }

 

    //删除对象

    public void delete(Object domain) {

        getHibernateTemplate().delete(domain);

    }

 

    

    //使用主键ID查询

    public T findById(Class<T> domainClass, ID id) {

        return getHibernateTemplate().get(domainClass, id);

    }

 

    

    //查询所有

    public List<T> findAll(Class<T> domainClass) {

        return getHibernateTemplate().loadAll(domainClass);

    }

 

    //命名查询

    public List<T> findByNamedQuery(String queryName, Object... values) {

        return getHibernateTemplate().findByNamedQuery(queryName, values);

    }

 

    //QBC

    public List<T> findByCriteria(DetachedCriteria criteria) {

        return getHibernateTemplate().findByCriteria(criteria);

    }

 

}

开发小技巧:

  1. 抽取代码的时候,也可以从实现类往接口上抽取。
  2. 抽取代码相对比较复杂一点,但只需要写一次,就可以供所有业务使用,说白了,就是实现了对通用方法的封装,不用每次在不同的Dao中写重复的代码。

 

 

附加:如果开发中使用命名查询,可以在hbm文件中配置命名查询语句 (Userinfo.hbm.xml)

<!-- 使用命名查询 -->

<query name="Userinfo.login">

        from Userinfo where name = ? and password = ?

</query>

 

 

  1. 统一配置(struts.xml,message.properties,applicationContext.xml)

配置几个:页面跳转、国际化、struts2、Spring核心配置文件。

第一步:配置 struts.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC

    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

    "http://struts.apache.org/dtds/struts-2.3.dtd">

 

<struts>

    <!--

        开发者模式:关闭:副作用:如果是ajax请求的情况下,一旦出错,不会在页面上打印错误

        所以采用struts.i18n.reload和struts.configuration.xml.reload的模式

    -->

<!-- <constant name="struts.devMode" value="true" /> -->

<!-- 不用重启服务器 -->

    <constant name="struts.i18n.reload" value="true" />

    <constant name="struts.configuration.xml.reload" value="true" />

    <!-- 表单样式 -->

    <constant name="struts.ui.theme" value="simple" />

    <!-- 加载src下的国际化文件messages.properties -->

    <constant name="struts.custom.i18n.resources" value="messages" />

 

<package name="default" namespace="/" extends="struts-default">

        <!-- 用户管理相关 -->

        <!-- <action name="user_*" class="cn.itcast.storemanager.web.action.UserAction" method="{1}"> -->

        <action name="user_*" class="userAction" method="{1}">

            <result name="loginjsp">/login.jsp</result>

            <result type="redirect">/jsps/main.jsp</result>

        </action>

</package>

 

</struts>

第二步:在src下配置messages.properties

UserAction.loginerror=\u60A8\u8F93\u5165\u7684\u7528\u6237\u540D\u6216\u5BC6\u7801\u4E0D\u6B63\u786E

显示:

第三步:配置applicationContext.xml

(1)事务管理中增加login方法的事务控制:

<!-- 配置事务 -->

    <!-- 配置事务管理器平台 -->

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory" ref="sessionFactory" />

    </bean>

    <!-- 事务管理通知 -->

    <tx:advice id="txAdvice" transaction-manager="transactionManager">

        <tx:attributes>

            <tx:method name="save*" read-only="false"/>

            <tx:method name="update*" read-only="false"/>

            <tx:method name="delete*" read-only="false"/>

            <tx:method name="find*" read-only="true"/>

            <tx:method name="login" read-only="true"/>

        </tx:attributes>

    </tx:advice>

    

    <!-- 切入点和切面 -->

    <aop:config>

        <aop:pointcut expression="bean(*Service)" id="txPointcut"/>

        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

    </aop:config>

(2)三层的Bean配置:

<!--通用的DAO类 -->

    <bean id="userDao" class="cn.itcast.storemanager.dao.impl.GenericDaoImpl">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    

    <!-- service -->

    <bean id="userService" class="cn.itcast.storemanager.service.impl.UserServiceImpl">

        <property name="userDao" ref="userDao"/>

    </bean>

    

    <!-- action -->

    <bean id="userAction" class="cn.itcast.storemanager.web.action.UserAction" scope="prototype">

        <property name="userService" ref="userService"/>

    </bean>

整体运行登录测试:

在数据库中新增用户admin,密码admin,登录测试功能。

 

  1. 仓库管理功能的实现(单表的CRUD)

分析:仓库表不依赖于其他表,是独立的单表操作。下面我们实现仓库表的CRUD

为了实现业务,我们要编写的内容:

  1. 仓库添加功能

一般开发功能基本三步:

页面---java代码逻辑—配置文件

页面:

第一步: 改造表单(/jsps/store/add.jsp):将页面改造 <s:form>     

<s:form action="store_add" namespace="/" method="post" name="select">

 

</s:form>

 

<tr>

                            <td style="padding-left:10px;" colspan="2" align="left">

                                <font color="red">

                                    <s:fielderror/>

                                    <s:actionerror/>

                                </font>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                仓库名称:

                            </td>

                            <td>

                                <s:textfield name="name" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                仓库地址:

                            </td>

                            <td>

                                <s:textfield name="addr" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                库管员:

                            </td>

                            <td>

                                <s:textfield name="manager" cssClass="tx"/>

                            </td>

                        </tr>

测试:

刷新页面,看是否添加页面显示正常。

 

第二步: 编写服务器端java代码

编写逻辑分析:我们的目标是增删改查:

  • 增加、删除、修改 -----> 重定向查询的Action
  • 查询的Action(重新查询数据) -----> 转发列表jsp页面

 

第三步:编写StoreAction:

//仓库管理的action

public class StoreAction extends BaseAction<Store>{

    //注入service

    private IStoreService storeService;

    public void setStoreService(IStoreService storeService) {

        this.storeService = storeService;

    }

 

 

    //保存仓库

    @InputConfig(resultName="addInput")//用于指定input进行struts2的校验

    public String add(){

        //业务层保存操作

        storeService.saveStore(model);

//        return "listjsp";//跳转到列表

        return "storelist";//先查列表//使用重定向技术

    }

}

第四步:编写IStoreService 接口

//仓库的业务层接口

public interface IStoreService {

 

    /**

     * 保存仓库

     * @param store

     */

    public void saveStore(Store store);

}

第五步:编写StoreServiceImpl 代码

//仓库管理的业务层实现

public class StoreServiceImpl extends BaseService implements IStoreService{

    //注入dao

    private IGenericDao<Store, String> storeDao;

    public void setStoreDao(IGenericDao<Store, String> storeDao) {

        this.storeDao = storeDao;

    }

 

    

    //@CacheEvict(value="storemanager",allEntries=true)//清除缓存区域的所有对象

    public void saveStore(Store store) {

        storeDao.save(store);

    }

 

第六步: 配置struts.xml

<!-- 仓库管理相关 -->

        <action name="store_*" class="storeAction" method="{1}">

            <!-- 列表页面 -->

            <result name="listjsp">/jsps/store/store.jsp</result>

            <!-- 列表查询action -->

            <result name="storelist" type="redirectAction">store_list</result>

            <!-- 添加仓库 -->

            <result name="addInput">/jsps/store/add.jsp</result>

</action>

第七步:配置applicationContext.xml

<!--通用的DAO类 -->

    <bean id="userDao" class="cn.itcast.storemanager.dao.impl.GenericDaoImpl">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    <bean id="storeDao" class="cn.itcast.storemanager.dao.impl.GenericDaoImpl">

        <property name="sessionFactory" ref="sessionFactory"/>

    </bean>

    

    <!-- service -->

    <bean id="userService" class="cn.itcast.storemanager.service.impl.UserServiceImpl">

        <property name="userDao" ref="userDao"/>

    </bean>

    <bean id="storeService" class="cn.itcast.storemanager.service.impl.StoreServiceImpl">

        <property name="storeDao" ref="storeDao"/>

    </bean>

    

    <!-- action -->

    <bean id="userAction" class="cn.itcast.storemanager.web.action.UserAction" scope="prototype">

        <property name="userService" ref="userService"/>

    </bean>

    <bean id="storeAction" class="cn.itcast.storemanager.web.action.StoreAction" scope="prototype">

        <property name="storeService" ref="storeService"/>

    </bean>

注意:Action是多例。

 

第八步:表单校验

    局部校验: Action所在包 "Action类名-<action>标签name属性-validation.xml"。

(1)新建xml文件:

引入配置文件头部:

位置:xwork-core-2.3.15.3.jar中的xwork-validator-1.0.3.dtd中

详细验证规则可以参考:

(2)配置:StoreAction-store_add-validation.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC

        "-//Apache Struts//XWork Validator 1.0.3//EN"

        "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

        

<validators>

    <!-- 仓库的名称 -->

    <field name="name">

        <field-validator type="requiredstring">

            <message key="StoreAction.name.required"/>

        </field-validator>

    </field>

    <!-- 仓库的地址 -->

    <field name="addr">

        <field-validator type="requiredstring">

            <message key="StoreAction.addr.required"/>

        </field-validator>

    </field>

    <!-- 仓库的管理员 -->

    <field name="manager">

        <field-validator type="requiredstring">

            <message key="StoreAction.manager.required"/>

        </field-validator>

    </field>

 

</validators>

 

(2)修改messages.properties

UserAction.loginerror=\u60A8\u8F93\u5165\u7684\u7528\u6237\u540D\u6216\u5BC6\u7801\u4E0D\u6B63\u786E

StoreAction.name.required=\u4ED3\u5E93\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A

StoreAction.addr.required=\u4ED3\u5E93\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A

StoreAction.manager.required=\u4ED3\u5E93\u7684\u7BA1\u7406\u5458\u4E0D\u80FD\u4E3A\u7A7A

 

视图查看:

 

(5)在add.jsp页面中 添加 <s:fielderror> 回显标签。

<tr>

        <td style="padding-left:10px;" colspan="2" align="left">

            <font color="red">

                <s:fielderror/>

                <s:actionerror/>

            </font>

        </td>

</tr>

另外,还需要配置校验错误的默认返回的input结果集。

<!-- 仓库管理相关 -->

        <action name="store_*" class="storeAction" method="{1}">

            <!-- 列表页面 -->

            <result name="storelist">/jsps/store/store.jsp</result>

            <!-- 列表查询action -->

            <result name="listjsp" type="redirectAction">store_list</result>

            <!-- 添加仓库 校验出错跳转的页面-->

            <result name="input">/jsps/store/add.jsp</result>

</action>

 

重启服务进行测试:

分别不填写和填写仓库信息,看是否能正常保存到数据库。

添加一条数据,到数据库查看是否存在。

 

  1. 仓库列表查询功能

流程:点击查看仓库 跳转store_list.action 先查询 到jsp显示。

第一步:先修改左侧导航菜单的链接:修改main.jsp

在main.jsp中引入struts2的标签库,将仓库管理连接的普通标签改为struts2标签:

<s:a namespace="/" action="store_list.action" target="content" id="left1002">[仓库管理]</s:a>

 

第二步:在StoreAction 添加 list查询方法

//仓库管理的action

public class StoreAction extends BaseAction<Store>{

    //注入service

    private IStoreService storeService;

    public void setStoreService(IStoreService storeService) {

        this.storeService = storeService;

    }

 

 

    //保存仓库

    @InputConfig(resultName="addInput")//用于指定input进行struts2的校验

    public String add(){

        //业务层保存操作

        storeService.saveStore(model);

//        return "listjsp";//跳转到列表

        return "storelist";//先查列表

    }

    

    //列表查询仓库

    public String list(){

        List<Store> list = storeService.findAllStore();

        //list放入map

        putValueStackMap("list", list);

        //返回列表页面

        return "storejsp";

    }

}

提示:记得将save的Action的返回结果修改回storelist。

 

第三步:修改IStoreService接口

/**

     * 查询所有仓库列表

     * @return

     */

    public List<Store> findAllStore();实现

第四步:修改StoreServiceImpl类

public List<Store> findAllStore() {

    return storeDao.findAll(Store.class);

}

 

 

修改列表页面store.jsp来回显数据

<tr style="background:#D2E9FF;text-align: center;">

    <td>名称</td>

        <td>地址</td>

        <td>管理员</td>

        <td>操作</td>

</tr>

                                

<s:iterator value="#list" >                                    

<tr>

        <td><s:property value="name"/></td>

        <td><s:property value="addr"/></td>

        <td><s:property value="manager"/></td>

        <td>

            <s:a action="store_editview" namespace="/">

            <s:param name="id" value="id"/>

                    修改

            </s:a>

            <s:a action="store_delete" namespace="/" cssClass="delLink">

            <s:param name="id" value="id"/>

                    删除

            </s:a>

        </td>        

    </tr>

</s:iterator>

 

重启服务测试:

 

  1. 仓库删除功能

思路:根据主键id,拼接超链接来删除:<a href="….?id=xxx"> 删除 </a>

 

第一步:修改store.jsp 页面删除链接

<s:a action="store_delete" namespace="/" cssClass="delLink">

    <s:param name="id" value="id"/>

        删除

</s:a>

第二步:在StoreAction 添加delete 方法

//删除仓库

    public String delete(){

        //调用业务层

        storeService.deleteStore(model);

        //返回到列表

        return "storelist";

    }

第三步:IStoreService接口 代码

/**

     * 根据id删除仓库

     * @param store

     */

    public void deleteStore(Store store);

第四步:StoreServiceImpl类的代码

    public void deleteStore(Store store) {

 

        storeDao.delete(store);

    }

 

 

第五步:重启服务测试删除。

 

【提示】:如果部分较旧的浏览器可能会报js错误,那么换个较新的浏览器试试。

以后大家在开发过程中,一定要注意浏览器的兼容性问题,在交付项目、上线之前,要用客户的所用的浏览器测试一下。

 

【思考】:

如果仓库中存在货物,能否删除 ???

可能无法删除,可能报错!!

原因是:有主外键关联。

 

【功能优化】:

在页面添加 JS确认删除效果。

store.jsp

<s:a action="store_delete" onclick="return confirm('是否确认删除')">

 

  1. 仓库修改功能

分析修改,两步:需要先查询回显数据,再进行修改提交

因此,功能开发需要分两部分:表单回显和更新提交。我们分别来做。

 

第一部分:表单回显(查询)。

第一步:修改 store.jsp 修改超链接(参考删除)

<s:a action="store_editview" namespace="/">

        <s:param name="id" value="id"/>

        修改

</s:a>

 

第二步:在StoreAction 添加 editview 方法

//修改的回显

    public String editview(){

        //根据id查询出来数据,压入栈顶

        Store store=storeService.findStoreById(model.getId());

        pushToValueStackRoot(store);

        //跳转到修改页面

        return "editjsp";

    }

 

第三步:业务层IStoreServcie接口:

/**

     * 根据id查询仓库

     * @param id

     * @return

     */

    public Store findStoreById(String id);

 

第四步:业务层StoreServcieImpl:

public Store findStoreById(String id) {

        return storeDao.findById(Store.class, id);

    }

 

第五步:配置struts.xml 跳转编辑页面

<!-- 仓库管理相关 -->

        <action name="store_*" class="storeAction" method="{1}">

            <!-- 列表页面 -->

            <result name="listjsp">/jsps/store/store.jsp</result>

            <!-- 列表查询action -->

            <result name="storelist" type="redirectAction">store_list</result>

            <!-- 添加仓库 -->

            <result name="addInput">/jsps/store/add.jsp</result>

            <!-- 修改页面 -->

            <result name="editjsp">/jsps/store/edit.jsp</result>

            <result name="editInput">/jsps/store/edit.jsp</result>

</action>

 

第六步:修改edit.jsp页面, 将<form> 改造为 <s:form>,使其拥有回显功能。

<s:form action="store_edit" namespace="/" method="post" name="select">

 

</s:form>

思考:修改表单和添加表单的区别是什么?

就差一个主键id。因此,修改需要隐藏一个id,用来传递id执行更新。

【补充】实际业务开发中,你可以将保存和修改页面用一个。(就根据判断页面有没有传过来id来判断是修改还是保存)-课外补充-回去试试。

<tr>

                <td bgcolor="a0c0c0" style="padding-left:10px;" colspan="2" align="left">

                    <b>修改新仓库:</b>

                </td>

</tr>

                        <tr>

                            <td style="padding-left:10px;" colspan="2" align="left">

                                <font color="red">

                                    <s:fielderror/>

                                    <s:actionerror/>

                                </font>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                仓库名称:

                            </td>

                            <td>

                                <s:textfield name="name" cssClass="tx"/>

                                <!-- id隐藏域 -->

                                <s:hidden name="id"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                仓库地址:

                            </td>

                            <td>

                                <s:textfield name="addr" cssClass="tx"/>

                            </td>

                        </tr>

                        <tr>

                            <td>

                                库管员:

                            </td>

                            <td>

                                <s:textfield name="manager" cssClass="tx"/>

                            </td>

                        </tr>

第七步:回显阶段功能测试,测试点:

  • 查看修改页面是否能数据填充回显;
  • 用firebug等工具或者查看页面源代码查看id是否正常;

 

第二部分:数据更新提交。

第一步:在edit.jsp中编辑form表单

<s:form action="store_edit" namespace="/" method="post" name="select">

 

</s:form>

 

第二步:修改form 提交,StoreAction添加 edit方法

//修改仓库数据

    @InputConfig(resultName="editInput")

    public String edit(){

        //将数据传入业务层

        storeService.updateStore(model);

        //跳转到列表页面

        return "storelist";

        

    }

第三步:业务层IStoreService代码

/**

     * 更新仓库

     * @param store

     */

    public void updateStore(Store store);

第四步:业务层StoreServiceImpl.java

public void updateStore(Store store) {

        //hibernate自动根据id来修改所有字段的

        storeDao.update(store);

    }

提示:

这里的修改直接调用update方法,得必须保证store的属性都有值。

 

第五步:测试修改功能。

 

 

功能优化补充:增加表单校验功能。

增加校验文件:(将添加的校验文件再复制一份,重命名为)

页面上增加:

<tr>

                            <td bgcolor="a0c0c0" style="padding-left:10px;" colspan="2" align="left">

                                <b>修改新仓库:</b>

                            </td>

                        </tr>

                        <tr>

                            <td style="padding-left:10px;" colspan="2" align="left">

                                <font color="red">

                                    <s:fielderror/>

                                    <s:actionerror/>

                                </font>

                            </td>

                        </tr>

发现问题:

如果修改方法也添加了校验, 默认跳转input视图, 但添加方法已经配置了input,怎么办?

解决办法:修改默认input视图,让修改和添加的校验失败后分别跳到不同的input --------- @InputConfig,可以通过resultName属性指定不同input的名称。

 

(1)修改storeAction,添加注解@InputConfig。

//保存仓库

    @InputConfig(resultName="addInput")//用于指定input进行struts2的校验

    public String add(){

        //业务层保存操作

        storeService.saveStore(model);

//        return "listjsp";//跳转到列表

        return "storelist";//先查列表

    }

 

//修改仓库数据

    @InputConfig(resultName="editInput")

    public String edit(){

        //将数据传入业务层

        storeService.updateStore(model);

        //跳转到列表页面

        return "storelist";

        

    }

 

配置struts.xml,分别对增加和修改配置不同input视图 。

<!-- 仓库管理相关 -->

        <action name="store_*" class="storeAction" method="{1}">

            <!-- 列表页面 -->

            <result name="listjsp">/jsps/store/store.jsp</result>

            <!-- 列表查询action -->

            <result name="storelist" type="redirectAction">store_list</result>

            <!-- 添加仓库 -->

            <result name="addInput">/jsps/store/add.jsp</result>

            <!-- 修改页面 -->

            <result name="editjsp">/jsps/store/edit.jsp</result>

            <result name="editInput">/jsps/store/edit.jsp</result>

        </action>

再次测试校验失败的页面跳转功能。

 

 

 

  1. 货物管理功能的实现-入库(仓库列表的AJAX请求,Spring和Ehcache缓存集成)

 

分析一下功能:

入库主要是将货物保存到仓库中,那么保存货物是需要选择仓库的,因此,下面我们在做入库功能之前,先要实现仓库列表的显示。

 

 

  1. 仓库列表查询显示

点击【入库】

 

【分析】

有两种方案:

  1. 打开这个页面的时候,在值栈中放入仓库的列表对象,页面使用ongl表达式,获取到列表对象,让下拉自己显示数据

<!-- ongl表达式获取列表(list中是很多map)将List<Store> 存放到map栈,命名为storeList -->

<s:select list="#storeList" name="storeId" id="storeId" listKey="id" listValue="name"></s:select>

2.将下拉列表看成一个独立组件,该组件的数据独立加载,和外面的业务无关。

这里采用第二种方式。

选择仓库列表,通常更多使用Ajax交互获取

    异步交互:(json和xml) 最常用的数据格式 json ({key:value} 对象, [elements] 数组 )

 

第一步:在系统页面 导入jQuery

    /jsps/save/save.jsp 入库页面

<!-- 引入jquery库 -->

<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"></script>

 

第二步: 基于一个事件,发生Ajax请求 :(这里是页面加载后填充列表数据---大家也可以使用点击事件)

Jquery Ajax 最常用的两个操作 $.get 、$.post (如果有参数,则需要用post;没有参数,则随意,一般我们都用post)

我们这里可以省略请求参数和返回内容格式,只需要请求地址和回调函数即可,代码如下:

<script type="text/javascript">

    $(function(){

        //页面加载后,发生ajax请求

        /**

        url,[data],[callback],[type]String,Map,Function,String

        url:发送请求地址。

        data:待发送 Key/value 参数。请求的数据

        callback:发送成功时回调函数。

        type:返回内容格式,xml, html, script, json, text, _default

        */

        $.post("${pageContext.request.contextPath}/store_listAjax.action",function(data){

            //data是服务器返回的内容(原本是json字符串),经过转换后的对象(js对象或js数据对象)。

            

            

        });

        

        

    });

    

</script>

 

第三步: 服务器处理数据,将处理结果生成json,返回客户端。

分析:其实这个异步请求仓库数据的方法与查询所有仓库数据的方法基本是一致的,只是返回结果不同。一个是返回List集合到跳转页面,一个是返回json字符串。

在StoreAction 添加 listAjax方法:

//查询所有列表(给异步请求用的)

    public String listAjax(){

        

        return "listajax";    

}

     那么如何将查询结果转换为json?

可以使用struts2提供json插件包(struts2-json-plugin.jar),完成值栈中数据,到json转换返回。

 

    手动导入插件包:

 

 

查看 struts-plugin.xml(了解插件的基本作用)

    该插件编写一个新的package (json-default) ,并且内部提供 json 结果集类型 ,只要使用json结果集类型,struts2 将数据转换为json格式返回客户端 !

 

编写StoreAction,将查询的集合存放到root栈的result属性中。

//查询所有列表(给异步请求用的)

    public String listAjax(){

                //查询所有仓库,返回list

        List<Store> storeList = storeService.findAllStore();

        //怎样让list转化成json?

        /**

         * 第一步: struts2-json-plugin-2.3.15.3.jar

         * 第二步:修改struts.xml配置:<package name="default" namespace="/" extends="json-default">

         * 第三步:修改struts.xml文件:添加返回值:<result name="listajax" type="json"></result>

         * 第四步:list压入栈顶

         * */

        pushValueStackRoot(storeList);

        return "listajax";    

    }

 

在struts.xml 配置结果集:

更改默认包:(要求我们的包去继承json-default包)

<package name="default" namespace="/" extends="json-default">

 

</package>

思考:

我们要将哪些数据转换为json?

分析一下JsonResult(org.apache.struts2.json.JSONResult) 源码,关联struts2源码来查看(课件中的SSH整合原始包中有相应zip包):

====================================================================

转换成json

    如果为结果集配置root参数,去值栈找root参数值对应对象,转换为json返回,如果没有配置root参数, 将root栈的顶部对象返回(默认model在栈顶,如果没有model,则action在栈顶) !

struts.xml

<!-- 仓库管理相关 -->

        <action name="store_*" class="storeAction" method="{1}">

            <!-- 列表页面 -->

            <result name="listjsp">/jsps/store/store.jsp</result>

            <!-- 列表查询action -->

            <result name="storelist" type="redirectAction">store_list</result>

            <!-- 添加仓库 -->

            <result name="addInput">/jsps/store/add.jsp</result>

            <!-- 修改页面 -->

            <result name="editjsp">/jsps/store/edit.jsp</result>

            <result name="editInput">/jsps/store/edit.jsp</result>

            

            <!-- json结果集类型 -->

            <result name="listajax" type="json">

            

            </result>

        </action>

 

测试调试:

通过firebug抓包工具查看数据是否返回 !

提示:如果配置的是开发者模式,则这里会返回200,而不是错误500。

控制台打印了异常:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.itcast.storemanager.domain.Store.goodses, no session or session was closed

原因是:查询store的时候,其集合属性goods默认是延迟加载的,而store返回到表现层action的时候,事务已经在service层结束了,也说明连接和session关了。

思考:可以用OpenSessionInView么?

可以解决报错,但不合适,因为,我们不需要goods关联集合属性的数据,只是需要store即可。性能会降低。

 

解决方案: 使用@JSON注解,排除不需要转的属性 --- 添加getXxx方法上:

在Store的po类上的getGoodses上添加注解:表示注解排除不需要转换成json的属性数据,即Store对象关联的Goods对象集合。

    //表示注解排除不需要转换成json的属性数据

    @JSON(serialize=false)

    public Set getGoodses() {

        return this.goodses;

    }

正确

 

第四步: 完善ajax代码:编写客户端回调函数,处理返回数据

给select列表元素增加id属性,用于选择这个select:

<tr>

                            <td>

                                选择仓库:

                            </td>

                            <td>

                                <select class="tx" style="width:120px;" name="store.id" id="store_id">

                                    

                                </select>

                                (此信息从数据库中加载)

                            </td>

                        </tr>

【回顾知识点】我们前台页面拿到的是个数组,那么如何在jQuery中遍历数组:

第五步:加载仓库的异步请求代码:

<!-- 引入jquery库 -->

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.8.3.js"></script>

<script type="text/javascript">

    //onload

    $(function(){

        //dom初始化后加载

        //$.post(请求url,请求参数,回调函数,返回的类型);

        $.post("${pageContext.request.contextPath}/store_listAjax.action",function(data){

            //data:转换后的对象:json对象数组-dom对象

            $(data).each(function(){

                //this每一个对象json

                //this.id

                var option=$("<option value='"+this.id+"'>"+this.name+"</option>");

                //添加到下拉列表中

                $("#store_id").append(option);

            });

        

        });

    });

 

</script>

 

 

  1. Spring和Ehcache缓存集成

业务问题:如果仓库不经常变动,大量进出库,总是需要查询仓库列表 (列表重复) ,使用缓存优化 !

 

spring缓存的配置原理:(注解)

 

 

第一步: 导入ehcache的jar 和 ehcache.xml

    ehcache-core-2.6.6.jar

spring-context-support.jar (spring支持jar,包含平台缓存管理器)

第二步:在resources下创建ehcache.xml文件,配置自定义的缓存名称<cache name="storemanager">

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

 

<diskStore path="java.io.tmpdir"/>

<defaultCache

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="true"

maxElementsOnDisk="10000000"

diskPersistent="false"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU"

/>

<cache name="storemanager"

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

overflowToDisk="true"

maxElementsOnDisk="10000000"

diskPersistent="false"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU"

/>

</ehcache>

 

 

第三步: 配置Spring的applicationContext.xml

引用cache 名称空间

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:cache="http://www.springframework.org/schema/cache"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/cache

http://www.springframework.org/schema/cache/spring-cache.xsd">

    

 

第四步:注解方式完成配置:(重点)

两步:

  1. 在核心配置文件中开启缓存驱动,然后在需要缓存或清除缓存的方法上加注解

    <!-- 配置缓存整合 -->

        <!-- ehcache管理器对象 -->

        <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">

            <!-- 默认读取classpath:ehcache.xml -->

            <property name="configLocation" value="classpath:ehcache.xml"/>

        </bean>

        <!-- 具体的平台缓存管理器 -->

        <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">

            <!-- 注入ehcache -->

            <property name="cacheManager" ref="ehCacheManager"/>

        </bean>

        <!-- 缓存注解驱动 -->

        <cache:annotation-driven cache-manager="cacheManager"/>

     

    (2)在StoreServiceImpl.java类中,使用注解开启缓存

    代码:

    查询方法需要从缓存中查询

    @Cacheable(value="storemanager")//放入缓存

        public List<Store> findAllStoreList() {

            return storeDao.findAll(Store.class);

        }

    增删改方法可以清理缓存

    @CacheEvict(value="storemanager",allEntries=true)//清除缓存区域的所有对象

        public void saveStore(Store store) {

            storeDao.save(store);

        }

     

    【扩展】,可将不同业务放置到不同的缓存,比如,将系统参数、分类,货物、仓库。。。。

     

    1. 重点和作业

     

    1. 架构搭建---登录功能---代码抽取—三层抽取---开发方法(先编码—再配置)
    2. Crud的编程:基本crud的步骤(值栈的使用,阻止默认事件),jquery引入
    3. 入库:ajax加载(页面加载后的加载—可以改造更懒加载)
    4. 缓存的使用

     

     

     

posted @ 2017-01-10 22:21  beyondcj  阅读(415)  评论(0编辑  收藏  举报