1.1.1 介绍
加载properties
配置数据源DataSource
配置SessionFactory , 加载所有hbm.xml
hibernate事务管理
使用 <import > 所有的模块都使用单独配置文件
1.1.2 使用源码包
使用config源码,将源码和配置文件分开存放,方便程序的维护。
1.1.3 spring核心
1.1.3.0约束
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
1.1.3.1 加载properties
<!-- 公共配置文件,web.xml配置加载核心文件 --> <!-- 1 加载properties文件 --> <context:property-placeholder location="classpath:jdbcinfo.properties"/>
1.1.3.2 配置数据源
<!-- 2 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
1.1.3.3 配置hibernate sessionFactory
<!-- 3 配置hibernate SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <!-- 3.1 配置数据源 * <property name="属性名" ref="另一个bean引用"> name 必须是对象的setter方法推断获得,setDataSource(...), 去掉set DataSource ,首字母小写 dataSource * ref 其他bean引用 <bean id=""> 可以任意,一般情况与上面属性名称相同。 --> <property name="dataSource" ref="dataSource"></property> <!-- 3.2 配置hibernate其他属性 * 在hibernate.cfg.xml 配置文件 “hibernate.dialect” 和 “dialect” 等效的 * 在spring配置文件中,必须使用“hibernate.dialect” --> <property name="hibernateProperties"> <props> <!-- 方言 --> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <!-- 显示sql语句 --> <prop key="hibernate.show_sql">true</prop> <!-- 格式化sql语句 --> <prop key="hibernate.format_sql">true</prop> </props> </property> <!-- 3.3 加载映射文件 com/itheima/crm/staff/domain/CrmStaff.hbm.xml com/itheima/crm/post/domain/CrmPost.hbm.xml com/itheima/crm/*/domain/*.hbm.xml --> <property name="mappingLocations" value="classpath:com/itheima/crm/*/domain/*.hbm.xml"></property> </bean>
1.1.3.4 配置 hibernate 事务管理
<!-- 4 事务管理 --> <!-- 4.1 事务管理器,spring事务必须在事务管理器平台上工作 * 在hibernate中事务需要session,session是从sessionFactory中获取的。 --> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 4.2 事务通知,确定事务的详情,明确切入点使用什么样的事务 * 项目约定:service层方法名称 所有添加必须add开头 所有更新必须update开头 所有删除必须delete开头 所有查询必须find开头 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="add*"/> <tx:method name="save*"/> <tx:method name="update*"/> <tx:method name="delete*"/> <tx:method name="find*" read-only="true"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <!-- 4.3 aop编程,确定切入点 * 所有的service层,需要进行事务管理 com.itheima.crm.staff.service.impl com.itheima.crm.classes.service.impl com.itheima.crm.*.service.. --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.crm.*.service..*.*(..))"/> </aop:config>
在struts.xml 配置“公共模块”,使用include包含子模块,所有的子模块都继承“公共模块”
struts.xml
<struts> <!-- struts 核心配置,公共内容 --> <!-- 1 常量 --> <!-- 1.1 开发模式 --> <constant name="struts.devMode" value="true"></constant> <!-- 1.2 struts 标签主题, simple 表示 没有风格,使用struts标签为了回显 --> <constant name="struts.ui.theme" value="simple"></constant> <!-- 2 公共模块,共有的内容都配置此包中 --> <package name="common" namespace="/" extends="struts-default"> </package> <!-- 包含其他模块 --> <include file="struts/struts-staff.xml"></include> </struts>
struts-staff.xml
<struts> <!-- 每一个模块 # 单独配置员工 , 所有的模块将使用“struts.xml.公共模块”--> <package name="crm_sta" namespace="/staff" extends="common"> </package> </struts>
<!-- spring * 设置初始化参数,确定spring配置文件位置 * 使用监听器去加载配置文件,并将spring容器存放到ServletContext作用域 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- struts * 配置前端控制器,服务器在启动时,初始化init(FilerConfig)将自动调用 * struts在初始化方法中,将自动的加载 classpath:struts.xml 文件 (src/config 两个源码目录都表示 类路径 classpath) --> <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>
编写PO类(已经实现)
dao执行用户查询(用户名和密码)
继承 HibernateDaoSupport,需要spring注入SessionFactory
service事务管理(已经实现)
注意:dao和service实现,需要配置到 applicationContext_staff.xml
action
1获得数据
属性驱动--action属性 (<input name="username" /> --> action 类 setUsername(String username) )
属性驱动--javabean (<inputname="user.username"> --> action类 setUesr(User user))
模型驱动 ModelDriven (1.实现接口 2编写javabean实例(new) 3 实现getModel方法并返回实例)
2.使用service 进行登录
3.处理信息
登录成功: session作用域记录登录状态,重定向登录成功页
不成功:当前请求记录错误信息(request作用域),使用请求转发,在登录页面显示
注意:action 需要配置到 struts-staff.xml
规定:所有的jsp页面(除登录页)存在WEB-INF下,javaee规范规定,浏览器端不能直接访问WEB-INF目录下的内容(tomcat直接过滤掉了),使用请求转发(<resulttype="dispatcher">)在服务器内部完成使用java程序读取内容,没有限制。
2.1 dao
dao实现 必须继承 HibernateDaoSupport
使用HibernateTemplate 提供find查询所有
public class StaffDaoImpl extends HibernateDaoSupport implements StaffDao { @Override public CrmStaff find(CrmStaff staff) { List<CrmStaff> list = this.getHibernateTemplate() .find("from CrmStaff where loginName = ? and loginPwd = ?" , staff.getLoginName(),staff.getLoginPwd()); if(list != null && list.size() > 0){ return list.get(0); } return null; } @Override public List<CrmStaff> findAll(String condition, Object[] params) { String hql = "from CrmStaff where 1=1 " + condition; return this.getHibernateTemplate().find(hql , params); } }
登录员工密码需要使用MD5加密
public class StaffServiceImpl implements StaffService { private StaffDao staffDao; public void setStaffDao(StaffDao staffDao) { this.staffDao = staffDao; } @Override public CrmStaff login(CrmStaff staff) { //登录密码需要加密 staff.setLoginPwd(MyStringUtils.getMD5Value(staff.getLoginPwd())); return staffDao.find(staff); } }MD5加密Utils
public class MyStringUtils { /** * 将提供的数据进行md5加密 * * 理论上不可逆的 * * JDK提供工具进行 消息摘要算法(加密) * @param value * @return */ public static String getMD5Value(String value){ try { //1 获得工具类 MessageDigest messageDigest = MessageDigest.getInstance("MD5"); //2 加密,加密结果为10进制 byte[] md5ValueByteArray = messageDigest.digest(value.getBytes()); //3 将10机制转换成16进制 BigInteger bigInteger = new BigInteger(1 , md5ValueByteArray); //4 转换 return bigInteger.toString(16); } catch (Exception e) { //如果有异常,不加密 return value; } } }
2.3 配置spring
<!-- 员工配置文件,一般情况:service、dao --> <!-- 1 dao --> <bean id="staffDao" class="com.itheima.crm.staff.dao.impl.StaffDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 2 service --> <bean id="staffService" class="com.itheima.crm.staff.service.impl.StaffServiceImpl"> <property name="staffDao" ref="staffDao"></property> </bean>
2.4 登录页面
首页:使用forward包含登录页
<jsp:forwardpage="/WEB-INF/pages/login.jsp"></jsp:forward>
登录页,将html表单,使用struts标签进行修改
略
2.5 action编写
2.5.1 封装数据
实现Model接口
//1 封装数据 private CrmStaff staff = new CrmStaff(); //1.2 提供javabean实例(一定要new) @Override public CrmStaff getModel() { //1.3 实现方法 return staff; }
2.5.2 提供service setter方法
action提供service属性名,必须与spring配置 service名称一致的
//2 spring 自动按照名称进行注入 // 2.1 员工service // 2.1 员工service private StaffService staffService; public void setStaffService(StaffService staffService) { this.staffService = staffService; } // 2.2部门service private DepartmentService departmentService; public void setDepartmentService(DepartmentService departmentService) { this.departmentService = departmentService; } // 2.3 职务 private PostService postService; public void setPostService(PostService postService) { this.postService = postService; }
2.5.3 登录
getSession().put() 相当于添加session作用域
addFieldError给指定的标签设置错误提示信息,标签的主题为simple,信息将不显示,需要使用在jsp<s:fieldEroor> 显示所有
this.addActionMessage() jsp 显示 <s:actionMessage> (可选)
public String login(){ // 登录 CrmStaff loginStaff = staffService.login(staff); // 处理 if(loginStaff != null){ //登录成功 -- session作用域数据,重定向首页 ActionContext.getContext().getSession().put("loginStaff", loginStaff); return "success"; } else { //不成功 -- 登录页给出提示 this.addFieldError("", "登录用户和密码不匹配"); return "login"; } }
2.5.4 登录成功
显示WEB-INF目录下页面
/** * 登录成功页面 * @return */ public String home(){ return "home"; }
2.6 action配置
<struts> <!-- 每一个模块 # 单独配置员工 , 所有的模块将使用“struts.xml.公共模块”--> <package name="crm_sta" namespace="/staff" extends="common"> <action name="staffAction_*" class="com.itheima.crm.staff.web.action.StaffAction" method="{1}"> <!-- 1 登录成功 ,重定向 成功页(action) dispatcher ,默认值,请求转发到jsp redirect,重定向到jsp redirectAction,重定向到另一个action chain,链 请求转发到另一个action --> <result name="success" type="redirectAction">staffAction_home</result> <!-- 2 login 在登录页中显示信息 <result name="login">/WEB-INF/pages/login.jsp</result> --> <!-- 3 成功页面 --> <result name="home">/WEB-INF/pages/frame.jsp</result> <!-- 4 input 用于处理系统默认错误 --> <result name="input">/WEB-INF/pages/login.jsp</result> <!-- 5 查询所有 --> <result name="findAll">/WEB-INF/pages/staff/listStaff.jsp</result> </action> </package> </struts>
思想:如何统一显示WEB-INF目录下的jsp页面?
在struts.xml 编写共有内容
<!-- 2.1 UIAction统一显示 WEB-INF下的jsp页面 # uiAction_*_* , 第一个星表示文件夹名称,第二个星表示jsp文件文件 # class 默认值 ActionSupport 1) struts-default.xml <default-class-ref class="com.opensymphony.xwork2.ActionSupport" /> 2) actionSupport 默认方法和返回值 public String execute() throws Exception { return SUCCESS; } # result.name 默认值 success --> <action name="uiAction_*_*"> <result>/WEB-INF/pages/{1}/{2}.jsp</result> </action>
编写登录拦截器,除了登录功能,其他的页面必须登录才能访问
拦截器编写:
1.编写实现类
功能:判断session是否保存登录用户信息,如果没有拦截,如果有放行。
2.配置拦截器
2.1将自定义拦截器注册给struts
2.2使用自定义拦截器,排除登录功能。
实现类回顾
Interceptor接口规定拦截器(1初始化、2拦截、3销毁),必须编写3个方法
AbstractInterceptor拦截器抽象实现类,只需要编写“拦截”方法即可
MethodFilterInterceptor方法过滤拦截器,使用此拦截器,在配置拦截器可以设置不需要拦截的方法。
http://localhost:8080/crm/index.jsp 当访问首页时,不使用自定义拦截器栈。
原因:struts 拦截器 值拦截action,不拦截jsp。
3.1 实现类
继承 MethodFiledInterceptor 可以在配置文件中设置需要过滤的方法,多个方法使用逗号分隔。
//登录拦截器 public class LoginInterceptor extends MethodFilterInterceptor { /* private String methodName; public void setMethodName(String methodName) { this.methodName = methodName; } */ @Override protected String doIntercept(ActionInvocation invocation) throws Exception { /* String method = invocation.getProxy().getMethod(); if(methodName.equals(method)){ //放行 return invocation.invoke(); } */ //判断用户登录状态 Object loginStaff = ActionContext.getContext().getSession().get("loginStaff"); if(loginStaff == null){ /**友好提示 start*/ Object action = invocation.getAction(); if(action instanceof ActionSupport){ ActionSupport actionSupport = (ActionSupport) action; actionSupport.addFieldError("", "请登录"); } /**友好提示 end*/ //没有登录 return "login"; } //放行 return invocation.invoke(); } }
3.2 配置拦截器
将自定义拦截器,与struts 默认拦截器栈 组合成一个新的栈
将自定义拦截器栈,配置自定义默认拦截器栈
<!-- 2.2 拦截器 -->
<!-- 2.2 拦截器 --> <interceptors> <!-- 2.2.1 注册拦截器(名称和实现类) --> <interceptor name="loginInterceptor" class="com.itheima.crm.web.interceptor.LoginInterceptor"></interceptor> <!-- 2.2.2 自定义拦截器栈 --> <interceptor-stack name="crmStack"> <!-- 1) 默认拦截器栈 --> <interceptor-ref name="defaultStack"></interceptor-ref> <!-- 2) 其他拦截器(token令牌:重置表单重复提交) --> <!-- 3) 自定义拦截器 * 设置<param name="属性"> 将执行拦截器实现类的setter方法 <param name="methodName">login</param> * excludeMethods 给父类设置需要排除的方法们,多个方法使用逗号分隔 --> <interceptor-ref name="loginInterceptor"> <param name="excludeMethods">login</param> </interceptor-ref> </interceptor-stack> </interceptors> <!-- 2.2.3 将自定义栈 配置 默认栈 <default-interceptor-ref name="crmStack"></default-interceptor-ref> -->
3.3 扩展
手动编写拦截器忽略方法见实现类代码/**/注释内容
登录拦截器,将拦截所有内容,如果没有登陆,大家共用一个result,需要配置全局结果。
<!-- 2.2.4 全局结果 --> <global-results> <result name="login">/WEB-INF/pages/login.jsp</result> </global-results>
编程式:在action 编写
所有方法:必须实现接口Validateable ,实现方法 invalidate()方法,校验所有的方法
单个方法:必须实现接口Validateable ,编写特殊的方法 invalidateAdd() 方法,表示值校验add方法
声明式:使用xml配置的
所有方法:
文件名称:action类名-validation.xml
单个方法:
1)文件名称:action类名-action配置名称-validation.xml
例如:http://localhost:8080/crm/staff/staffAction_login.action
action配置名称 --> staffAction_login
实例:对login方法校验,StaffAction-staffAction_login-validation.xml
2)文件约束:
xwork-core-2.3.15.3.jar/xwork-validator-1.0.3.dtd
<?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> </validators>
3)文件内容:
类型:xwork-core-2.3.15.3.jar/com/opensymphony/xwork2/validator/validators/default.xml
<validators> <!-- 1 登录用户 --> <field name="loginName"> <!-- 1.1 必填 --> <field-validator type="requiredstring"> <message>登录用户名不能为空</message> </field-validator> <!-- 1.2 长度--> <field-validator type="stringlength"> <param name="maxLength">14</param> <param name="minLength">2</param> <message>登录用户名必须在${minLength}-${maxLength}字符之间</message> </field-validator> </field> <!-- 登录密码 --> <!-- 1 登录用户 --> <field name="loginPwd"> <!-- 1.1 必填 --> <field-validator type="requiredstring"> <message>登录密码不能为空</message> </field-validator> <!-- 1.2 长度--> <field-validator type="stringlength"> <param name="maxLength">16</param> <param name="minLength">4</param> <message>登录密码必须在${minLength}-${maxLength}字符之间</message> </field-validator> </field> </validators>
4)文件位置,与action类同包
5.1 dao层
@Override public List<CrmStaff> findAll() { return this.getHibernateTemplate().find("from CrmStaff"); }
5.2 service层
@Override public List<CrmStaff> findAll() { return staffDao.findAll(); }
5.3 action实现及配置
/crm/src/com/itheima/crm/staff/web/action/StaffAction.java
<!-- 5 查询所有 -->
<resultname="findAll">/WEB-INF/pages/staff/listStaff.jsp</result>
/** * 查询所有 * @return */ public String findAll(){ // 查询 List<CrmStaff> allStaff = staffService.findAll(); // 将查询结果存放 值栈中 -- root --> jsp页面获取, key 或 属性名 获得内容 // * 如果一组数据(List) ,使用root set方法 ,设置key // * 如果一个对象(javabean),使用root push方法 ,javabean属性 ActionContext.getContext().getValueStack().set("allStaff", allStaff); return "findAll"; }
配置结果
<!-- 5 查询所有 --> <result name="findAll">/WEB-INF/pages/staff/listStaff.jsp</result>
在web.xml 必须spring提供过滤器,必须放置struts过滤器之前
<!-- spring 提供过滤器 --> <filter> <filter-name>openSession</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>openSession</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
5.5 员工管理--条件查询
5.5.1 查询所有部门
5.5.1.1 dao层
略
5.5.1.2 service层
略
5.5.1.3 配置spring
略
5.5.2 通过部门id查询职务
5.5.2.1 dao层
略
5.5.2.2 service层
略
5.5.2.3 spring 配置
略
5.5.3 优化
在web.xml中配置加载所有spring文件
applicationContext*.xml 使用*通配符
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext*.xml</param-value> </context-param>
注意:applicationContext.xml 不需要配置 <import>
使用ajax发送部门的id,查询对应的所有的职务
/crm/post/postAction_findAllWithDepartment?crmDepartment.depId=
将查询的结果转成成json数据,使用javascript遍历数据并展示。
使用Jsonlib 将 List<CrmPost>转成字符串
5.5.4.1 发送ajax
<script type="text/javascript"> function showPost(obj) { //1 获得选中的部门id var depId = obj.value; //2 发送ajax数据,通过部门的id查询职务 // * 路径参考 /crm/post/postAction_findAllWithDepartment?crmDepartment.depId= // 2.1 创建对象(ajax引擎) var xmlhttp = null; if (window.XMLHttpRequest){ xmlhttp=new XMLHttpRequest(); } else if (window.ActiveXObject){ xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } // 2.2 设置回调 xmlhttp.onreadystatechange = function(){ if(xmlhttp.readyState == 4 && xmlhttp.status == 200){ // # 获得select对象 var selectObj = document.getElementById("postSelectId"); selectObj.innerHTML = "<option value=''>--请选择职务--</option>"; //1)获得数据(字符串) var data = xmlhttp.responseText; //2) 转成json对象 var jsonData = eval("("+data+")"); //3)遍历 for(var i = 0 ; i < jsonData.length ; i ++){ var postObj = jsonData[i]; // 3.1)获得id var postId = postObj.postId; // 3.2)获得名称 var postName = postObj.postName; // #添加数据 selectObj.innerHTML += "<option value='"+postId+"'>"+postName+"</option>"; } } }; // 2.3 建立连接 var url = "${pageContext.request.contextPath}/post/postAction_findAllWithDepartment?crmDepartment.depId=" + depId; xmlhttp.open("get",url); // 2.4 发送请求 xmlhttp.send(null); } </script>
5.5.4.2 action生成json数据
使用jsonlib 需要导入jar包
/** * ajax 通过部门id查询职务,发送json * @return * @throws IOException */ public String findAllWithDepartment() throws IOException{ //1 查询 List<CrmPost> allPost = postService.findAll(post.getCrmDepartment().getDepId()); //2 jsonlib 将指定数据转发json字符串 //JSONObject 处理java对象 (map、javabean) //JSONArray 处理java容器(数组、List、Set) // 2.1 排除不需要字段 JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setExcludes(new String[] {"crmDepartment","crmStaffs"}); // 2.2 转换 String jsonStr = JSONArray.fromObject(allPost,jsonConfig).toString(); // 2.3 将json数据发送给浏览器 ServletActionContext.getResponse().setContentType("application/json;charset=UTF-8"); ServletActionContext.getResponse().getWriter().print(jsonStr); return NONE; //表示没有返回值 }
5.5.5 条件查询实现
使用ognl表达式串联进行数据封装,通过staff一个可以获得需要的所有数据。
根据staff进行条件查询
staff被 ModelDriven拦截器已经压入到栈顶,struts标签回显时,从栈顶开始获得数据,如果有就回显。
5.5.5.1 jsp表单
奇偶样式
<%--遍历数据,奇偶行样式不同 class="tabtd1" ,class="tabtd2" --%> <s:iterator value="allStaff" status="vs"> <%--将遍历每一项压入到栈顶(root),可以通过javabean 属性获得值 --%> <tr class="<s:property value="#vs.odd ? 'tabtd1' : 'tabtd2' "/>">
5.5.5.2 action实现类
/** * 查询所有 * @return */ public String findAll(){ // 1 查询所有员工 // 查询 List<CrmStaff> allStaff = staffService.findAll(staff); // 将查询结果存放 值栈中 -- root --> jsp页面获取, key 或 属性名 获得内容 // * 如果一组数据(List) ,使用root set方法 ,设置key // * 如果一个对象(javabean),使用root push方法 ,javabean属性 ActionContext.getContext().getValueStack().set("allStaff", allStaff); //2 查询所有的部门 List<CrmDepartment> allDepartment = this.departmentService.findAll(); // * 将数据存放到context , jsp获取 “#key” ActionContext.getContext().put("allDepartment", allDepartment); //3 如果选中部门,将查询部门的所有的职务 String depId = null; if(staff.getCrmPost() != null && staff.getCrmPost().getCrmDepartment() != null){ depId = staff.getCrmPost().getCrmDepartment().getDepId(); if(StringUtils.isNotBlank(depId)){ //选中部门 List<CrmPost> allPost = postService.findAll(depId); ActionContext.getContext().getValueStack().set("allPost", allPost); } } return "findAll"; }
5.5.5.3 service
使用 StringBuilder 拼凑 HQL语句,条件中都是“ and 属性 符号 ?” 在最后使用 “where 1=1 ”
List拼凑实际参数,(有序,可重复)
@Override public List<CrmStaff> findAll(CrmStaff staff) { //拼凑条件 //* 条件 StringBuilder sqlBuilder = new StringBuilder(); //* 参数 List<Object> paramsList = new ArrayList<Object>(); //有可能有条件 if(staff.getCrmPost() != null){ //2 选择职务(有职务就不添加部门) if(StringUtils.isNotBlank(staff.getCrmPost().getPostId())){ sqlBuilder.append(" and crmPost = ?"); paramsList.add(staff.getCrmPost()); } else { //1 选中部门 CrmDepartment department = staff.getCrmPost().getCrmDepartment(); if(department != null && StringUtils.isNotBlank(department.getDepId())){ sqlBuilder.append(" and crmPost.crmDepartment = ?"); paramsList.add(staff.getCrmPost().getCrmDepartment()); } } } //3员工姓名 null "" if(StringUtils.isNotBlank(staff.getStaffName())){ sqlBuilder.append(" and staffName like ?"); paramsList.add("%"+staff.getStaffName()+"%"); } // 将转成需要数据 String condition = sqlBuilder.toString(); Object[] params = paramsList.toArray(); return staffDao.findAll(condition , params); }
6.1 dao层
所有的dao继承 HibernateDaoSupport ,通过底层创建HibernateTemplate 进行PO操作。
save添加、update更新、delete删除
find查询所有,get |load 通过id查询
public class CourseTypeDaoImpl extends HibernateDaoSupport implements CourseTypeDao { @Override public void save(CrmCourseType courseType) { this.getHibernateTemplate().save(courseType); } @Override public void update(CrmCourseType courseType) { this.getHibernateTemplate().update(courseType); } @Override public void delete(CrmCourseType courseType) { this.getHibernateTemplate().delete(courseType); } @Override public List<CrmCourseType> findAll() { return this.getHibernateTemplate().find("from CrmCourseType"); } @Override public CrmCourseType findById(String id) { return this.getHibernateTemplate().get(CrmCourseType.class, id); } }
6.2 service层
提供 service 层 主要用于事务管理和业务处理。
略
6.3 配置spring
略
6.4 查询所有
6.4.1 action实现
略
6.4.2 struts配置
注意:将单独文件需要添加到 struts.xml中
略
6.4.3 jsp遍历
<%--数据展示,单行:tabtd1;双行:tabtd2 --%> <%--如果使用var,将查询结果存放context key=var value=遍历某一项 ,标签体中获取的方式 “#key” * 注意:如果使用var iterator迭代 在context存放一份,也在root中也存放一份。 --%> <s:iterator value="data" var="courseType"> <tr class="tabtd1"> <td align="center"><s:property value="#courseType.courseName" /></td> <td align="center"><s:property value="#courseType.remark" /></td> <td align="center"><s:property value="#courseType.total" /></td> <td align="center"><s:property value="#courseType.courseCost" /></td> <td width="11%" align="center"> <s:a namespace="/courseType" action="courseTypeAction_addOrUpdateUI"> <s:param name="courseTypeId" value="#courseType.courseTypeId"></s:param> <img src="${pageContext.request.contextPath}/images/button/modify.gif" class="img" /> </s:a> </td> </tr> </s:iterator>
hibernate提供saveOrUpdate 保存或更新。
代理主键:(hibernate自动设置)
如果没有OID,将执行save
如果有OID,将执行update
自然主键:(手动设置)
执行之前必须查询select
如果查询没有结果,将执行save
如果查询有结果,将执行update
添加 jsp页面没有id值,表达没有数据
更新 jsp页面需要id值,提供hidden 隐藏域存放id值。需要通过id查询标签回显(struts标签)
6.5.1 dao
@Override public void saveOrUpdate(CrmCourseType courseType) { this.getHibernateTemplate().saveOrUpdate(courseType); }
6.5.2 service
@Override public void saveOrUpdateCourseType(CrmCourseType courseType) { this.courseTypeDao.saveOrUpdate(courseType); }
6.5.3 action
需要提供UI显示页面,需要通过id查询
/** * 添加或编辑 页面显示 * @return */ public String addOrUpdateUI(){ //如果有id表示更新 -- 通过id查询类别 if(StringUtils.isNotBlank(courseType.getCourseTypeId())){ // 通过id查询 CrmCourseType findCourseType = courseTypeService.findById(courseType.getCourseTypeId()); // 必须将对象压入到指定 ActionContext.getContext().getValueStack().push(findCourseType); } return "addOrUpdateUI"; }
/** * 添加或更新操作 * @return */ public String addOrUpdate(){ courseTypeService.saveOrUpdateCourseType(courseType); return "addOrUpdate"; }
6.5.4 struts配置
<!-- 添加或更新成功 ,重定向查询 * 给结果类实现类设置属性值 --> <result name="addOrUpdate" type="redirectAction"> <param name="namespace">/courseType</param> <param name="actionName">courseTypeAction_findAll</param> </result>
6.5.5 jsp表单
更新时,需要提供隐藏域,此处必须使用if语句。如果没有添加 courseTypeId="" 不能添加成功
<s:form namespace="/courseType" action="courseTypeAction_addOrUpdate"> <%--提供隐藏域,更新时使用 --%> <s:if test="courseTypeId != null"> <s:hidden name="courseTypeId"></s:hidden> </s:if>
将使用自定义javabean封装分页的数据
PageBean
public class PageBean<T> { //必须 private int pageNum; //当前页(第几页) private int pageSize; //每页显示数据 private int totalRecord; //总记录数 //计算 private int startIndex; //开始索引 private int totalPage; //总页数 //分页结果 private List<T> data; //分页数据 // 动态显示导航条 private int start; private int end; public PageBean(int pageNum, int pageSize, int totalRecord) { super(); this.pageNum = pageNum; this.pageSize = pageSize; this.totalRecord = totalRecord; //1 算法:开始索引 this.startIndex = (this.pageNum - 1) * this.pageSize; //2 算法:总页数 this.totalPage = (this.totalRecord + this.pageSize - 1) / this.pageSize; //3 处理导航条 (显示10个分页) this.start = 1; this.end = 10; // totalPage = 4; if(this.totalPage <= 10){ this.start = 1; this.end = this.totalPage; } else { // totalPage = 38 ,要求:前4后5 this.start = this.pageNum - 4; this.end = this.pageNum + 5; // 第一页 if(this.start < 1){ this.start = 1; this.end = 10; } // 最后一页 if(this.end > this.totalPage){ this.end = this.totalPage; this.start = this.totalPage - 9; } } } get set...............
public class PageHibernateCallBack<T> implements HibernateCallback<List<T>> { private String hql; //hql语句 private Object[] params; //hql对应实际参数 private int firstResult; //开始索引 private int maxResults; //每页显示个数 public PageHibernateCallBack(String hql, Object[] params, int firstResult, int maxResults) { super(); this.hql = hql; this.params = params; this.firstResult = firstResult; this.maxResults = maxResults; } @Override public List<T> doInHibernate(Session session) throws HibernateException, SQLException { //1 创建Query对象 Query queryObject = session.createQuery(hql); //2 设置实际参数 if (params != null) { for (int i = 0; i < params.length; i++) { queryObject.setParameter(i, params[i]); } } //3 分页 // 3.1 开始索引 if (firstResult >= 0) { queryObject.setFirstResult(firstResult); } // 3.2 每页显示个数 if (maxResults > 0) { queryObject.setMaxResults(maxResults); } return queryObject.list(); } }
6.6.1 分页
service层将 web层传递(pageNum,pageSize)进行处理并封装到PageBean中,需要查询总记录
//1 总记录数 int totalRecord = this.courseTypeDao.getTotalRecord(condition,params); //2 将查询结果封装 javabean PageBean<CrmCourseType> pageBean = new PageBean<CrmCourseType>(pageNum, pageSize, totalRecord); //3 查询分页数 List<CrmCourseType> data = this.courseTypeDao.findAll(condition,params, pageBean.getStartIndex(), pageSize); pageBean.setData(data); return pageBean;
6.6.2 条件查询(拼凑条件)
查询总记录时,需要传递条件
查询分页数据时,也需要传递条件
在service拼凑条件即可。
@Override public PageBean<CrmCourseType> findAll(CrmCourseType courseType, int pageNum, int pageSize) { //拼凑条件 //#1 准备对象 StringBuilder builder = new StringBuilder(); List<Object> paramsList = new ArrayList<Object>(); //#2 拼凑 //#2.1 类别名称 if(StringUtils.isNotBlank(courseType.getCourseName())){ builder.append(" and courseName like ? "); paramsList.add("%"+courseType.getCourseName()+"%"); } //#2.2 简介 if(StringUtils.isNotBlank(courseType.getRemark())){ builder.append(" and remark like ? "); paramsList.add("%"+courseType.getRemark()+"%"); } //#2.3 总学时--条件都是字符串,需要的整形 int totalStart = 0; int totalEnd = 0; if(StringUtils.isNotBlank(courseType.getTotalStart())){ //200 totalStart = Integer.parseInt(courseType.getTotalStart()); } if(StringUtils.isNotBlank(courseType.getTotalEnd())){ //100 totalEnd = Integer.parseInt(courseType.getTotalEnd()); } // * 处理start <= end int temp = 0; if(totalStart > totalEnd){ /* totalStart = totalStart + totalEnd; totalEnd = totalStart - totalEnd; totalStart = totalStart - totalEnd; */ temp = totalStart; totalStart = totalEnd; totalEnd = temp; //查询条件中没有交换 } // * 拼凑sql if(totalStart > 0){ builder.append(" and total >= ?"); paramsList.add(totalStart); } if(totalEnd > 0){ builder.append(" and total <= ?"); paramsList.add(totalEnd); } //#2.4 费用 if(StringUtils.isNotBlank(courseType.getLessonCostStart())){ builder.append(" and courseCost >= ?"); paramsList.add(Double.parseDouble(courseType.getLessonCostStart())); } if(StringUtils.isNotBlank(courseType.getLessonCostEnd())){ builder.append(" and courseCost <= ?"); paramsList.add(Double.parseDouble(courseType.getLessonCostEnd())); } //#3 转换 String condition = builder.toString(); Object[] params = paramsList.toArray(); //1 总记录数 int totalRecord = this.courseTypeDao.getTotalRecord(condition,params); //2 将查询结果封装 javabean PageBean<CrmCourseType> pageBean = new PageBean<CrmCourseType>(pageNum, pageSize, totalRecord); //3 查询分页数 List<CrmCourseType> data = this.courseTypeDao.findAll(condition,params, pageBean.getStartIndex(), pageSize); pageBean.setData(data); return pageBean; }
6.6.3 jsp处理
使用javascript,将分页的数据,与表单中条件数据 一并发送给服务器
或分页a标签一并把条件发送过去
分页jsp
<table border="0" cellspacing="0" cellpadding="0" align="center"> <tr> <td align="right"> <span>第<s:property value="pageNum"/>/<s:property value="totalPage"/>页</span><br/> <span> <s:if test="pageNum>1"> <s:a namespace="/courseType" action="courseTypeAction_findAll">[首页] <s:param name="pageNum" value="1"></s:param> </s:a> <s:a namespace="/courseType" action="courseTypeAction_findAll">[上一页] <s:param name="pageNum" value="pageNum-1"></s:param> </s:a> </s:if> <!-- 1234..分页条 --> <s:iterator begin="start" end="end" var="m"> <s:a namespace="/courseType" action="courseTypeAction_findAll"> <s:property value="#m"/> <s:param name="pageNum" value="#m"></s:param> </s:a> </s:iterator> <s:if test="pageNum<totalPage"> <!-- <s:a namespace="/courseType" action="courseTypeAction_findAll">[下一页] <s:param name="pageNum" value="pageNum+1"></s:param> </s:a> --> <!-- 分页使用JS将分页与条件一并提交表单 ↓--> <a href="javascript:void(0)" onclick="condition(<s:property value="pageNum+1"/>)">[下一页]</a> <!-- 分页使用JS将分页与条件一并提交表单↑ --> <s:a namespace="/courseType" action="courseTypeAction_findAll">[尾页] <s:param name="pageNum" value="totalPage"></s:param> </s:a> </s:if> </span> </td> </tr> </table> <!-- hiddenId --> </body> <script type="text/javascript"> function condition(pageNum){ //<s:hidden name="pageNum" id="hiddenId"></s:hidden> //将表单隐藏标签(pageNum)value赋值 document.getElementById("hiddenId").value=pageNum; //点击onclick提交 连接提交为<a href="javascript:void(0)" document.forms[0].submit(); } </script>
泛型 + 反射
在Dao层一般情况下,进行增删改查(CRUD)
添加:session.save(PO)
修改:session.update(PO)
删除:session.delete(PO)
添加或更新:session.saveOrUpdate(PO)
OID查询:session.get(Xxx.class,id) / session.load(Xxx.class,id)
HQL查询:session.createQuery(hql).list()
public interface BaseDao<T> { /** * 保存 * @param t */ public void save(T t); /** * 更新 * @param t */ public void update(T t); /** * 删除 * @param t */ public void delete(T t); /** * 保存或更新 * @param t */ public void saveOrUpdate(T t); /** * 通过id查询 * @param t */ public T findById(String id); /** * 查询所有 * @param t */ public List<T> findAll(); /** * 查询带有条件所有 * @param t */ public List<T> findAll(String hqlCondition,Object[] params); /** * 查询总记录数,带有条件 * @param hqlCondition * @param hqlParams * @return */ int findTotalRecord(String hqlCondition, Object[] hqlParams); /** * 查询带有条件,分页数据 * @param hqlCondition hql查询条件 * @param hqlParams 对应的实际参数 * @param startIndex 开始索引 * @param pageSize 每页显示个数 * @return */ List<T> findAll(String hqlCondition, Object[] hqlParams, int startIndex, int pageSize); }
BaseDao的实现,继承HibernateDaoSupport
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> { //具体操作PO类Class对象 private Class<T> beanClass; //具体操作PO类 全限定类名 private String className; public BaseDaoImpl() { //1 获得当前运行类的,具有泛型信息的父类, ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass(); //2 获得实际参数类型 beanClass = (Class)parameterizedType.getActualTypeArguments()[0]; //3 实际参数类型 全限定类名 className = beanClass.getName(); } @Override public void save(T t) { this.getHibernateTemplate().save(t); } @Override public void update(T t) { this.getHibernateTemplate().update(t); } @Override public void delete(T t) { this.getHibernateTemplate().delete(t); } @Override public void saveOrUpdate(T t) { this.getHibernateTemplate().saveOrUpdate(t); } @Override public T findById(String id) { return this.getHibernateTemplate().get(beanClass, id); } @Override public List<T> findAll() { return this.getHibernateTemplate().find("from " + className); } @Override public List<T> findAll(String hqlCondition, Object[] params) { String hql = "from " +className + " where 1=1 " + hqlCondition; return this.getHibernateTemplate().find(hql, params); } @Override public int findTotalRecord(String hqlCondition, Object[] hqlParams) { String hql = "select count(*) from " + className + " where 1=1 " + hqlCondition; List<Long> list = this.getHibernateTemplate().find(hql, hqlParams); return list.size() == 1 ? list.get(0).intValue() : 0 ; } @Override public List<T> findAll(String hqlCondition, Object[] hqlParams, int startIndex, int pageSize) { String hql = "from "+className+" where 1=1 " + hqlCondition; return this.getHibernateTemplate().execute(new PageHibernateCallback<T>(hql, hqlParams, startIndex, pageSize)); } }
7.3 使用
7.3.1 接口
public interface UserDao extends BaseDao<CrmUser> { /** * 使用用户名和密码进行查询/特有方法 * @param logonName * @param logonPwd 注意:密码需要MD5加密 * @return */ public CrmUser findUser(String logonName,String logonPwd); }
7.3.2 实现类
public class UserDaoImpl extends BaseDaoImpl<CrmUser> implements UserDao { @Override @SuppressWarnings("unchecked") public CrmUser findUser(String logonName, String logonPwd) { List<CrmUser> allUser = this.getHibernateTemplate() .find("from CrmUser c where c.logonName = ? and c.logonPwd = ?" ,logonName,logonPwd); return allUser.size() == 1 ? allUser.get(0) : null ; } }
public class BaseAction<T> extends ActionSupport implements ModelDriven<T> { // 0 使用反射,实例化 T public BaseAction() { try { //1 获得当前运行类,被参数化父类。例如:BaseAction<CrmClass> ParameterizedType parameterizedType = (ParameterizedType) this .getClass().getGenericSuperclass(); //2 获得具体类型, CrmClass ,获得第一个实际参数 // * 泛型可以有多个,所以提供的是数组获得。例如: AClass<A,B,C,D,E> @SuppressWarnings("unchecked") Class<T> crmClass = (Class<T>) parameterizedType.getActualTypeArguments()[0]; //3 实例化 new CrmClass(); t = crmClass.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } //1 封装数据 private T t; @Override public T getModel() { return t; } //2 注入使用到service(如果都存放父类,之后将越来越多,可以采取放置在子类中) //2.1 private ClassesService classesService; // ** 交予spring注入 public void setClassesService(ClassesService classesService) { this.classesService = classesService; } // ** 提供给子类调用 //3 分页数据 private int pageNum; private int pageSize = 2; //暂时固定的 public void setPageNum(int pageNum) { this.pageNum = pageNum; } //4 简化 值栈操作 // 4.1 root --set public void set(String key,Object o){ ActionContext.getContext().getValueStack().set(key, o); } // 4.2 root -- push public void push(Object o){ ActionContext.getContext().getValueStack().push(o); } // 4.3 context public void put(String key,Object value){ ActionContext.getContext().put(key, value); } // 4.4 context -- session public void putSession(String key,Object value){ ActionContext.getContext().getSession().put(key, value); } }
1.1 jsp页面
提供 <input type="file" name="xxx">
表单 <form method="post"enctype="multipart/form-data">
1.2 action提供属性
struts默认拦截器栈中 提供upload拦截器,完成文件上传功能。
提供特定的属性即可
Filexxx; 内容
StringxxxFileName; 名称
StringxxxContentType; 类型
action实例默认压入到栈顶的,如果使用ModelDriven javabean再压入
/** * 上传页面 * @return */ public String uploadUI(){ //通过id查询班级 CrmClass findClass = this.getClassesService().findById(this.getModel().getClassId()); //压入栈顶 this.push(findClass); return "uploadUI"; } private File schedule; //文件内容 private String scheduleFileName; //文件名称 private String scheduleContentType; //文件类型(没有用) public void setSchedule(File schedule) { this.schedule = schedule; } public void setScheduleFileName(String scheduleFileName) { this.scheduleFileName = scheduleFileName; } public void setScheduleContentType(String scheduleContentType) { this.scheduleContentType = scheduleContentType; } /** * 文件上传 * @return */ @InputConfig(resultName="uploadInput") //如果不使用注解,方法出错时,默认返回input public String upload(){ try { //将 文件保存硬盘中 -- 将课表的内容保存硬盘,文件名随机,没有扩展名 (及将一个文件拆分两部分:内容,文件名) //1 上传文件父目录 WEB-INF/upload String parentDir = ServletActionContext.getServletContext().getRealPath("/WEB-INF/upload"); String filePath = UUID.randomUUID().toString(); //2 保存文件 FileUtils.copyFile(schedule, new File(parentDir , filePath)); //3 保存 this.getClassesService().addUploadFile(this.getModel().getClassId() , filePath, scheduleFileName); return "upload"; } catch (IOException e) { e.printStackTrace(); return ERROR; } }
1.3 拦截器属性设置
通过给upload拦截器设置内容,确定上传文件允许扩展名
格式:<param name="拦截器.属性">.....</param>
<interceptor-ref name="crmStack"> <!-- 设置上传文件的扩展名 --> <param name="fileUpload.allowedExtensions">.xls,.xlsx</param> </interceptor-ref>
1.4 修改错误是结果集返回值
当程序出现错误时,workflow拦截器默认将返回input
在目标方法 添加注解 @InputConfig(resultName="xxxxx") 确定当前方法出错时的返回名称
@InputConfig(resultName="uploadInput") //如果不使用注解,方法出错时,默认返回input
1.5 action提供属性
action提供 struts 获得数据,必须提供getter 方法
//下载资源 private InputStream target; public InputStream getTarget() { return target; } //下载文件名,存在中文乱码 private String downloadFileName; public String getDownloadFileName() throws UnsupportedEncodingException { // 处理中文乱码 if(downloadFileName != null){ return new String(downloadFileName.getBytes("GBK"),"ISO-8859-1"); } return downloadFileName; } public String download(){ //1 通过id查询 CrmClass findClass = this.getClassesService().findById(this.getModel().getClassId()); //2 读取内容 InputStream is = ServletActionContext.getServletContext().getResourceAsStream("/WEB-INF/upload/" + findClass.getUploadPath()); if(is != null){ target = is; downloadFileName = findClass.getUploadFileName(); return "download"; } this.addFieldError("", "下载资源不存在"); return "error"; }
1.6 xml配置获得内容
<!-- 下载 --> <result name="download" type="stream"> <!-- 设置响应头 --> <param name="contentDisposition">attachment;filename=${downloadFileName}</param> <!-- 确定流 --> <param name="inputName">target</param> </result>