十、课程类别的分页(含条件)
10.1、流程分析
10.2、PageBean 实现分析
PageBean.java
package com.itheima.crm.page;
import java.util.List;
public class PageBean<T> {
/*
算法1:
if(totalRecord % pageSize == 0) {
totalPage = totalRecord / pageSize;
} else { // 半页
totalPage = totalRecord / pageSize + 1;
}
算法2:
totalPage = (totalRecord + (pageSize - 1)) / pageSize;
*/
// 必须项
private int pageNum; // 第几页(当前页)
private int pageSize; // 每页显示个数(固定值)
private int totalRecord; // 总记录数(查询数据库)
// 计算项
private int startIndex; // 开始索引(计算)
private int totalPage; // 总分页数(计算)
// 分页数据
private List<T> data; // 传过来什么就是什么
// 含参构造(含有3个参数的构造方法,这样就能告诉使用者需要这三个必选项)
public PageBean(int pageNum, int pageSize, int totalRecord) {
super();
this.pageNum = pageNum;
this.pageSize = pageSize;
this.totalRecord = totalRecord;
// 计算项
// 1、开始索引(索引从0开始)
this.startIndex = (this.pageNum -1) * this.pageSize;
// 2、总分页数
this.totalPage = (this.totalRecord + (this.pageSize - 1)) / this.pageSize;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalRecord() {
return totalRecord;
}
public void setTotalRecord(int totalRecord) {
this.totalRecord = totalRecord;
}
public int getStartIndex() {
return startIndex;
}
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}
10.3、dao层 实现分析
自定义实现类
PageHibernateCallback.java
package com.itheima.crm.page;
import java.sql.SQLException;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
public class PageHibernateCallback<T> implements HibernateCallback<List<T>> {
private String hql;
private Object[] params;
private int startIndex;
private int pageSize;
// 上面的4个参数如何传入进来呢?
// 方式一:增加4个setter方法,使用时new出这个类,然后调用4个setter方法,一个一个set进去即可
// 方式二:通过含参构造方法,使用时new出这个类,同时传入4个参数
// 方式三:非传统的set方法,即链式编程。步骤如下:
// 5.1、首先,只要setter方法,不要getter方法
// 5.2、返回值类型是自己,返回的是自己
// 5.3、使用时,先new出对象,再setXxx().setXxx().setXxx()...
public PageHibernateCallback<T> setHql(String hql) {
this.hql = hql;
return this;
}
public PageHibernateCallback<T> setParams(Object[] params) {
this.params = params;
return this;
}
public PageHibernateCallback<T> setStartIndex(int startIndex) {
this.startIndex = startIndex;
return this;
}
public PageHibernateCallback<T> setPageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}
@Override
public List<T> doInHibernate(Session session) throws HibernateException, SQLException {
// 1、通过HQL语句,获得Query对象
Query queryObject = session.createQuery(hql);
// 2、条件设置
for (int i = 0; i < params.length; i++) {
queryObject.setParameter(i, params[i]);
}
// 3、分页
queryObject.setFirstResult(startIndex);
queryObject.setMaxResults(pageSize);
// 4、查询所有
return queryObject.list();
}
}
CourseTypeDao.java
/**
* 分页,查询总记录数
*
* @param condition
* @param params
* @return
*/
public int getTotalRecord(String condition, Object[] params);
/**
* 分页,查询结果
*
* @param condition 条件
* @param params 条件的实际参数
* @param startIndex 开始索引
* @param pageSize 每页显示的个数
* @return
*/
public List<CrmCourseType> findAll(String condition, Object[] params, int startIndex, int pageSize);
CourseTypeDaoImpl.java
@Override
public int getTotalRecord(String condition, Object[] params) {
String hql = "select count(c) from CrmCourseType c where 1=1 " + condition;
List<Long> list = this.getHibernateTemplate().find(hql, params); // 聚合函数查询
return list.get(0).intValue(); // 包装类就是点方法,基本类就是强转
}
@Override
public List<CrmCourseType> findAll(String condition, Object[] params, int startIndex, int pageSize) {
String hql = "from CrmCourseType where 1=1 " + condition;
// HQL不支持分页,QBC支持分页,所以如果使用HQL,需要我们自定义实现类
return this.getHibernateTemplate().execute(new PageHibernateCallback<CrmCourseType>().setHql(hql).setParams(params).setStartIndex(startIndex).setPageSize(pageSize));
}
10.4、service层 实现分析
CourseTypeService.java
/**
* 带有条件的查询所有课程类别 + 分页
*
* @param courseType 条件
* @param pageNum 第几页(当前页)
* @param pageSize 每页显示个数
* @return
*/
public PageBean<CrmCourseType> findAllCourseType(CrmCourseType courseType, int pageNum, int pageSize);
CourseTypeServiceImpl.java
@Override
public PageBean<CrmCourseType> findAllCourseType(CrmCourseType courseType, int pageNum, int pageSize) {
// 1.1、使用StringBuilder 接收:拼凑查询条件
StringBuilder builder = new StringBuilder();
// 1.2、使用List 接收:拼凑实际参数,需要可以重复,有顺序 => 选择 List集合
List<Object> paramsList = new ArrayList<Object>();
// 2、过滤条件
// 2.1、过滤课程类别,不需要 null 或者 "" 或者 " ",使用工具类
if (StringUtils.isNotBlank(courseType.getCourseName())) {
builder.append(" and courseName like ?");
paramsList.add("%" + courseType.getCourseName() + "%");
}
// 2.2、过滤课程简介,不需要 null 或者 "" 或者 " ",使用工具类
if (StringUtils.isNotBlank(courseType.getRemark())) {
builder.append(" and remark like ?");
paramsList.add("%" + courseType.getRemark() + "%");
}
// 2.3、过滤总学时
if (StringUtils.isNotBlank(courseType.getTotalStart())) {
builder.append(" and total >= ?");
paramsList.add(Integer.parseInt(courseType.getTotalStart()));
}
if (StringUtils.isNotBlank(courseType.getTotalEnd())) {
builder.append(" and total <= ?");
paramsList.add(Integer.parseInt(courseType.getTotalEnd()));
}
// 2.3、过滤总费用
if (StringUtils.isNotBlank(courseType.getCourseCostStart())) {
builder.append(" and courseCost >= ?");
paramsList.add(Double.parseDouble(courseType.getCourseCostStart()));
}
if (StringUtils.isNotBlank(courseType.getCourseCostEnd())) {
builder.append(" and courseCost <= ?");
paramsList.add(Double.parseDouble(courseType.getCourseCostEnd()));
}
// 3、使用数据
// 1、条件,格式:“and ...? and ...?”
String condition = builder.toString();
// 2、实际参数
Object[] params = paramsList.toArray();
// 2、分页
// 2.1、总记录数 totalRecord
int totalRecord = this.courseTypeDao.getTotalRecord(condition, params);
// 2.2、创建PageBean对象
PageBean<CrmCourseType> pageBean = new PageBean<CrmCourseType>(pageNum, pageSize, totalRecord);
// 2.3、分页数据
List<CrmCourseType> data = this.courseTypeDao.findAll(condition, params, pageBean.getStartIndex(), pageBean.getPageSize());
pageBean.setData(data);
return pageBean;
}
10.5、jsp页面 实现分析
- 方案1
- 方案2
我们使用方案2。
/day36_06_Spring_crm/WebRoot/WEB-INF/pages/coursetype/listCourse.jsp
......
<%--条件查询 start --%>
<s:form namespace="/" action="courseTypeAction_findAll">
<%-- 提供隐藏域,存放的是需要的当前页 ,id是给js/css使用的,name是给提交表单用的 --%>
<s:hidden id="pageNum" name="pageNum" value="1"></s:hidden>
......
<td align="right">
<span>第<s:property value="#pageBean.pageNum"/>/<s:property value="#pageBean.totalPage"/>页</span>
<span>
<s:if test="#pageBean.pageNum gt 1"><%--gt 等价于 < --%>
<a href="javascript:void(0)" onclick="showPage(1)">[首页]</a> <%-- 首页 = 1 --%>
<a href="javascript:void(0)" onclick="showPage(<s:property value="#pageBean.pageNum - 1"/>)">[上一页]</a> <%-- 上一页 = 当前页 - 1 --%>
</s:if>
<s:if test="#pageBean.pageNum lt #pageBean.totalPage"><%--lt 等价于 > --%>
<a href="javascript:void(0)" onclick="showPage(<s:property value="#pageBean.pageNum + 1"/>)">[下一页]</a> <%-- 下一页 = 当前页 + 1 --%>
<a href="javascript:void(0)" onclick="showPage(<s:property value="#pageBean.totalPage"/>)">[尾页]</a> <%-- 尾页 --%>
</s:if>
</span>
</td>
</tr>
</table>
<script type="text/javascript">
function showPage(num) {
// 1、修改隐藏域的值
document.getElementById("pageNum").value = num;
// 2、提交表单
document.forms[0].submit();
}
</script>
......
10.6、jsp页面 动态显示效果
PageBean.java
......
// 增加动态显示条
private int start;
private int end;
// 含参构造(含有3个参数的构造方法,这样就能告诉使用者需要这三个必选项)
public PageBean(int pageNum, int pageSize, int totalRecord) {
super();
this.pageNum = pageNum;
this.pageSize = pageSize;
this.totalRecord = totalRecord;
// 计算项
// 1、开始索引(索引从0开始)
this.startIndex = (this.pageNum -1) * this.pageSize;
// 2、总分页数
this.totalPage = (this.totalRecord + (this.pageSize - 1)) / this.pageSize;
// 3、增加动态显示条
// 3.1、初始化数据,暂定显示10个分页
this.start = 1; // 开始的分页
this.end = 10; // 最后的分页
// 3.2、处理数据,假如:总分页数 totalPage = 4
if (this.totalPage <= 10) {
this.end = this.totalPage; // 最后的分页=总分页数
} else {
// 假如:总分页数 totalPage = 15
// 3.3、当前页:前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.start = this.totalPage - 9;
this.end = this.totalPage;
}
}
......
listCourse.jsp
......
<s:if test="#pageBean.pageNum gt 1"><%--gt 等价于 < --%>
<a href="javascript:void(0)" onclick="showPage(1)">[首页]</a> <%-- 首页 = 1 --%>
<a href="javascript:void(0)" onclick="showPage(<s:property value="#pageBean.pageNum - 1"/>)">[上一页]</a> <%-- 上一页 = 当前页 - 1 --%>
</s:if>
<%-- 添加动态显示条 --%>
<s:iterator begin="#pageBean.start" end="#pageBean.end" var="num">
<a href="javascript:void(0)" onclick="showPage(<s:property value="#num"/>)"><s:property value="#num"/></a>
</s:iterator>
<s:if test="#pageBean.pageNum lt #pageBean.totalPage"><%--lt 等价于 > --%>
<a href="javascript:void(0)" onclick="showPage(<s:property value="#pageBean.pageNum + 1"/>)">[下一页]</a> <%-- 下一页 = 当前页 + 1 --%>
<a href="javascript:void(0)" onclick="showPage(<s:property value="#pageBean.totalPage"/>)">[尾页]</a> <%-- 尾页 --%>
</s:if>
......
十一、this小案例
Parent.java
package com.itheima.testthis;
public class Parent {
public void init() {
System.out.println("1 parent init");
this.demo();
}
public void demo() {
System.out.println("2 parent demo");
}
}
Son.java
package com.itheima.testthis;
public class Son extends Parent {
public void init() {
super.init();
System.out.println("3 son init");
this.demo();
}
public void demo() {
System.out.println("4 son demo");
}
}
TestThis.java
package com.itheima.testthis;
public class TestThis {
public static void main(String[] args) {
// Parent parent = new Parent();
// parent.init();
// this在编译时指的是当前类,在运行时指的是当前运行类,对字段和方法的处理方式是不一样的。
Son son = new Son();
son.init();
}
}
测试结果
1 parent init
4 son demo
3 son init
4 son demo
十二、BaseDao
以前的做法:
现在的做法:
1. 将Dao层通用的方法进行统一实现。
2. 之后在dao层使用dao接口,即StaffDao。
3. StaffDao接口,需要继承 BaseDao接口,从而对外可以提供多个方法,即可以直接使用BaseDao的方法 + 自己特有的方法。
4. BaseDaoImpl实现类,需要继承 HibernateDaoSupport,从而可以调用HibernateTemplate,相当于之前直接编写dao实现类。同时需要实现BaseDao接口,从而将公共内容都完成。
5. StaffDaoImpl实现类,需要继承 BaseDaoImpl实现类,从而所有的公共内容都可以使用了。同时需要实现StaffDao接口,从而完成特有的功能。
示例代码如下:
BaseDao.java
package com.itheima.crm.base;
import java.util.List;
public interface BaseDao<T> {
public void save(T t);
public void update(T t);
public void delete(T t);
public void saveOrUpdate(T t);
public T findById(java.io.Serializable id);
public List<T> findAll();
// ......
}
BaseDaoImpl.java
package com.itheima.crm.base.impl;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.itheima.crm.base.BaseDao;
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
// 定义变量接收实际参数
private Class<?> beanClass;
// T 在编译时,只是变量,在运行是才可以获得具体的类型
public BaseDaoImpl() {
// 通过构造方法去获得运行时的类型,BaseDaoImpl<CrmStaff> 叫做被参数化的类型
ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
// 获得实际参数值,以下方法获得的是所有的实际参数值,但是我们此时只有一个
beanClass = (Class<?>) parameterizedType.getActualTypeArguments()[0];
}
@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(Serializable id) {
return (T) this.getHibernateTemplate().get(beanClass, id);
}
@Override
public List<T> findAll() {
// beanClass.getName() 获得的是类的全限定名称 例如:com.itheima.crm.staff.domain.CrmStaff
return this.getHibernateTemplate().find("from " + beanClass.getName());
}
// 回顾hql语句
// session.createQuery("from CrmStaff"); // 简便写法,存在自动导包
// session.createQuery("from com.itheima.crm.staff.domain.CrmStaff"); // 完整写法
}
以Staff进行举例:
StaffDao.java
package com.itheima.crm.staff.dao;
import com.itheima.crm.base.BaseDao;
import com.itheima.crm.staff.domain.CrmStaff;
public interface StaffDao extends BaseDao<CrmStaff> {
/**
* 通过用户名和密码查询员工
*
* @param loginName
* @param loginPwd
* @return
*/
public CrmStaff find(String loginName, String loginPwd);
}
StaffDaoImpl.java
package com.itheima.crm.staff.dao.impl;
import java.util.List;
import com.itheima.crm.base.impl.BaseDaoImpl;
import com.itheima.crm.staff.dao.StaffDao;
import com.itheima.crm.staff.domain.CrmStaff;
@SuppressWarnings("unchecked")
public class StaffDaoImpl extends BaseDaoImpl<CrmStaff> implements StaffDao {
@Override
public CrmStaff find(String loginName, String loginPwd) {
List<CrmStaff> allStaff = this.getHibernateTemplate().find("from CrmStaff where loginName=? and loginPwd=?", loginName, loginPwd);
if (allStaff.size() == 1) {
return allStaff.get(0);
}
return null;
}
}
十三、BaseAction
主要思想:
1、封装数据
2、让Spring一下子注入多个service
3、分页的数据
4、简化值栈操作
BaseAction.java
package com.itheima.crm.base;
import java.lang.reflect.ParameterizedType;
import com.itheima.crm.coursetype.service.CourseTypeService;
import com.itheima.crm.department.service.DepartmentService;
import com.itheima.crm.post.service.PostService;
import com.itheima.crm.staff.service.StaffService;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class BaseAction<T> extends ActionSupport implements ModelDriven<T> {
// 1、封装数据
private T t;
@Override
public T getModel() {
return t;
}
// 1.1、实例化T,可以通过反射去new出T
public BaseAction() {
try {
// 1、要先获得该类运行时的Class
ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
Class<T> clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0];
// 2、通过反射去创建实例
t = clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e); // 把编译时异常转换成运行时异常
}
}
// 2、让Spring一下子注入多个service
// 提供setter方法,是让Spring进行注入的
// 提供getter方法,是让子类可以获得Spring注入的对象的
// 2.1、员工的service
private StaffService staffService;
public void setStaffService(StaffService staffService) {
this.staffService = staffService;
}
public StaffService getStaffService() {
return staffService;
}
// 2.2、职务的service
private PostService postService;
public void setPostService(PostService postService) {
this.postService = postService;
}
public PostService getPostService() {
return postService;
}
// 2.3、部门的service
private DepartmentService departmentService;
public void setDepartmentService(DepartmentService departmentService) {
this.departmentService = departmentService;
}
public DepartmentService getDepartmentService() {
return departmentService;
}
// 2.4、课程类别的service
private CourseTypeService courseTypeService;
public void setCourseTypeService(CourseTypeService courseTypeService) {
this.courseTypeService = courseTypeService;
}
public CourseTypeService getCourseTypeService() {
return courseTypeService;
}
// 2.5、班级的service
// 3、分页的数据
// 第几页(当前页)
private int pageNum = 1;
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageNum() {
return pageNum;
}
// 每页显示个数(固定值)
private int pageSize = 2; // 固定值,不用提供setter方法
public void setPageSize(int pageSize) { // 如果写了setter方法,为了以后扩展留下接口
this.pageSize = pageSize;
}
public int getPageSize() {
return pageSize;
}
// 4、简化值栈操作
public void push(Object o) {
ActionContext.getContext().getValueStack().push(o);
}
public void set(String key, Object o) {
ActionContext.getContext().getValueStack().set(key, o);
}
public void put(String key, Object value) {
ActionContext.getContext().put(key, value);
}
public void putSession(String key, Object value) {
ActionContext.getContext().getSession().put(key, value);
}
public void putApplication(String key, Object value) {
ActionContext.getContext().getApplication().put(key, value);
}
}
十四、上传和下载
14.1、班级列表的查询
思路:
1. ClassesDao / ClassesDaoImpl --> 继承BaseDao / BaseDaoImpl
2. service
3. spring配置,完善BaseAction
4. ClassesAction --> 继承BaseAction
5. left.jsp --> struts.xml/struts-classes.xml --> listClass.jsp
1、
ClassesDao.java
package com.itheima.crm.classes.dao;
import com.itheima.crm.base.BaseDao;
import com.itheima.crm.classes.domain.CrmClasses;
public interface ClassesDao extends BaseDao<CrmClasses> {
}
ClassesDaoImpl.java
package com.itheima.crm.classes.dao.impl;
import com.itheima.crm.base.impl.BaseDaoImpl;
import com.itheima.crm.classes.dao.ClassesDao;
import com.itheima.crm.classes.domain.CrmClasses;
public class ClassesDaoImpl extends BaseDaoImpl<CrmClasses> implements ClassesDao {
}
2、
ClassesService.java
package com.itheima.crm.classes.service;
import java.util.List;
import com.itheima.crm.classes.domain.CrmClasses;
public interface ClassesService {
/**
* 查询所有班级
*
* @return
*/
public List<CrmClasses> findAll();
}
ClassesServiceImpl.java
package com.itheima.crm.classes.service.impl;
import java.util.List;
import com.itheima.crm.classes.dao.ClassesDao;
import com.itheima.crm.classes.domain.CrmClasses;
import com.itheima.crm.classes.service.ClassesService;
public class ClassesServiceImpl implements ClassesService {
private ClassesDao classesDao;
public void setClassesDao(ClassesDao classesDao) {
this.classesDao = classesDao;
}
@Override
public List<CrmClasses> findAll() {
return this.classesDao.findAll();
}
}
3、
applicationContext.xml
<!-- 4.5、班级 -->
<import resource="applicationContext-classess.xml"/>
applicationContext-classess.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: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">
<!-- 班级配置项 dao、service-->
<bean id="classesDao" class="com.itheima.crm.classes.dao.impl.ClassesDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="classesService" class="com.itheima.crm.classes.service.impl.ClassesServiceImpl">
<property name="classesDao" ref="classesDao"></property>
</bean>
</beans>
BaseAction.java
......
// 2.5、班级的service
private ClassesService classesService;
public void setClassesService(ClassesService classesService) {
this.classesService = classesService;
}
......
4、
ClassesAction.java
package com.itheima.crm.classes.web.action;
import java.util.List;
import com.itheima.crm.base.BaseAction;
import com.itheima.crm.classes.domain.CrmClasses;
public class ClassesAction extends BaseAction<CrmClasses> {
public String findAll() {
List<CrmClasses> allClasses = this.getClassesService().findAllClasses();
this.set("allClasses", allClasses);
return "findAll";
}
}
5、
/day36_06_Spring_crm/WebRoot/WEB-INF/pages/frame/left.jsp
d.add('010301','0103','班级管理','${pageContext.request.contextPath}/classesAction_findAll','','right');
struts.xml
<!-- 3.4、班级-->
<include file="struts/struts-classes.xml"></include>
struts-classes.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>
<!-- 班级的配置 -->
<package name="cla" namespace="/" extends="common">
<action name="classesAction_*" class="com.itheima.crm.classes.web.action.ClassesAction" method="{1}">
<!-- 1、查询所有班级 -->
<result name="findAll">/WEB-INF/pages/classes/listClass.jsp</result>
</action>
</package>
</struts>
/day36_06_Spring_crm/WebRoot/WEB-INF/pages/classes/listClass.jsp
......
<s:iterator value="allClasses" status="vs">
<tr class="<s:property value="#vs.even ? 'tabtd2' : 'tabtd1'"/>">
<td align="center"><s:property value="name"/></td>
<td align="center"><s:property value="courseType.courseName"/></td>
<td align="center"><s:date name="beginTime" format="yyyy-MM--dd"/></td>
<td align="center"><s:date name="endTime" format="yyyy-MM--dd"/></td>
<td align="center"><s:property value="status"/></td>
<td align="center"><s:property value="totalCount"/></td>
<td align="center"><s:property value="upgradeCount"/></td>
<td align="center"><s:property value="changeCount"/></td>
<td align="center">
<a href="${pageContext.request.contextPath}/pages/classesm/addOrEditClass.jsp">
<img src="${pageContext.request.contextPath}/images/button/modify.gif" class="img"/></a>
</td>
<td align="center">
<a href="${pageContext.request.contextPath}/pages/classesm/showClass.jsp">
<img src="${pageContext.request.contextPath}/images/button/modify.gif" class="img"/></a>
</td>
<td align="center" title="上次上传时间:2015-04-02">
<a href="${pageContext.request.contextPath}/pages/classesm/uploadClass.jsp">上传</a>
<a href="${pageContext.request.contextPath}/pages/classesm/downloadClass">下载</a> <br/>
</td>
</tr>
</s:iterator>
......
14.2、通过班级id查询班级详情
ClassesService.java
/**
* 通过班级id查询班级详情
*
* @return
*/
public CrmClasses findById(String classesId);
ClassesServiceImpl.java
@Override
public CrmClasses findById(String classesId) {
return this.classesDao.findById(classesId);
}
ClassesAction.java
public String uploadUI() {
CrmClasses findClasses = this.getClassesService().findById(this.getModel().getClassesId());
// 将查询到的数据压入栈顶(为了更方便的回显数据)
this.push(findClasses);
return "uploadUI";
}
struts-classes.xml
<!-- 2、显示上传表单页面 -->
<result name="uploadUI">/WEB-INF/pages/classes/uploadClass.jsp</result>
listClass.jsp
<s:a namespace="/" action="classesAction_uploadUI">
<s:param name="classesId" value="classesId"></s:param> <%--因为上传文件之前需要通过id查询班级详情 --%>
上传
</s:a>
uploadClass.jsp
......
<table width="88%" border="0" class="emp_table" style="80%;">
<tr>
<td width="120">课程类别:</td>
<td><s:property value="courseType.courseName"/></td>
<td> </td>
</tr>
<tr>
<td>班级:</td>
<td><s:property value="name"/></td>
<td> </td>
</tr>
<tr>
<td>上次上传时间:</td>
<td><s:date name="uploadTime"/></td>
<td> </td>
</tr>
<tr>
<td>选择课表:</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td colspan="3">
<input type="file" name="schedule" value="" />
</td>
</tr>
</table>
......
14.3、文件上传
0. 先通过班级id查询班级详情,如上
1. 替换表单
<form action="/crm2/classesm/classAction_upload.action" method="post" enctype="multipart/form-data">
<input type="file" name="schedule">
2. 编写action类,struts已经把文件上传功能都完成了,我们只需要把需要的东西拿来就可以了。
我们需要希望struts把内容注入给我们,则就在action类中提供需要的字段,再提供setter方法即可。
private File schedule; // 文件内容
private String scheduleFileName; // 文件名称
private String scheduleContentType; // 文件类型
3. 将课表保存到硬盘,将课表的文件路径、文件名称、更新时间,更新到数据库。
4. struts中拦截器的设置,因为我们上传的是课表。设置允许上传的文件类型。
5. 文件上传不成功。ClassesAction类中使用注解@InputConfig 开发,struts-classes.xml中添加相应的配置。
/**
* 文件(课表)上传
*
* @return
* @throws IOException
*/
@InputConfig(resultName="uploadInput")
public String upload() throws IOException {
6. 文件上传不成功时的错误信息的国际化显示。
1、
uploadClass.jsp
......
<s:form namespace="/" action="classAction_upload.action" enctype="multipart/form-data">
......
<s:file name="schedule"></s:file>
......
2、
ClassesAction.java
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
* @throws IOException
*/
@InputConfig(resultName="uploadInput")
public String upload() throws IOException {
// 1、将课表保存在硬盘(企业级开发中,图片会上传到图片服务器,图片服务器返回一个路径,我们把该路径保存在数据库中即可)
// 1.1、在tomcat下,位置在.../WEB-INF/upload/...
// 方式一:写死父目录,不好
// String parentDir = "D:learnJavaWebapache-tomcatapache-tomcat-9.0.7webappsday36_06_Spring_crmWEB-INF...";
// 方式二:文件父目录
String parentDir = ServletActionContext.getServletContext().getRealPath("/WEB-INF/upload");
// 1.2、我们希望文件名是一个32位的随机数,且没有扩展名,这样安全性更高
String fileName = MyStringUtils.getUUID();
// 1.3、保存操作
FileUtils.copyFile(schedule, new File(parentDir, fileName));
// 2、更新操作
this.getModel().setUploadPath("/WEB-INF/upload" + fileName);
this.getModel().setUploadFilename(scheduleFileName);
this.getModel().setUploadTime(new Date());
this.getClassesService().updateUpload(this.getModel());
return "upload";
}
3、
ClassesService.java
/**
* 更新上传
*
* @param model
*/
public void updateUpload(CrmClasses classes);
ClassesServiceImpl.java
@Override
public void updateUpload(CrmClasses classes) {
// 1、先查询 ,再更新,涉及到快照和一级缓存
CrmClasses findClasses = this.classesDao.findById(classes.getClassesId());
findClasses.setUploadFilename(classes.getUploadFilename());
findClasses.setUploadPath(classes.getUploadPath());
findClasses.setUploadTime(classes.getUploadTime());
}
更新页面的表单需要添加隐藏字段
uploadClass.jsp
<s:form namespace="/" action="classesAction_upload.action" enctype="multipart/form-data">
<!-- <form action="/crm2/classesm/classAction_upload.action" method="post" enctype="multipart/form-data"> -->
<%--添加隐藏字段,因为上传表单的时候,需要先根据id进行查询 --%>
<s:hidden name="classesId" value="%{classesId}"></s:hidden>
<table width="88%" border="0" class="emp_table" style="80%;">
4、
struts中拦截器的设置,因为我们上传的是课表。设置允许上传的文件类型。
<interceptors>
<!-- 2.2.1、声明(注册),将登录拦截器实现类配置给struts -->
<interceptor name="loginInterceptor" class="com.itheima.crm.web.interceptor.LoginInterceptor"></interceptor>
<!-- 2.2.2、 自定义拦截器栈 -->
<interceptor-stack name="loginStack">
<!-- 给默认的拦截器栈中的某一个拦截器注入内容
* 格式:拦截器名称.属性 值1,值2
allowedExtensions
allowedTypes -->
<interceptor-ref name="defaultStack">
<param name="fileUpload.allowedExtensions">.xls, .xlsx</param>
</interceptor-ref>
<!-- 自定义登录拦截器:需要配置对login()方法不进行拦截 ,需要进行注入
* excludeMethods 配置不包含的方法,多个方法使用逗号分隔 -->
<interceptor-ref name="loginInterceptor">
<param name="excludeMethods">login</param>
</interceptor-ref>
</interceptor-stack>
5、
文件上传不成功时的页面。使用注解@InputConfig
ClassesAction.java
/**
* 文件(课表)上传
*
* @return
* @throws IOException
*/
@InputConfig(resultName="uploadInput")
public String upload() throws IOException {
struts-classes.xml
<!-- 4、上传不成功页面 -->
<result name="uploadInput">/WEB-INF/pages/error.jsp</result>
6、
文件上传不成功时的错误信息的国际化显示。如下图所示:
界面不友好
在error.jsp中,添加错误信息提示
<font color="#f00">
系统繁忙,请稍后重试</br>
<s:fielderror></s:fielderror></br>
<%-- <s:actionerror/></br> --%>
<%-- <s:actionmessage/></br> --%>
</font>
界面还是不友好
需要进行国际化显示,在com.itheima.crm.classes.web.action包中新建ClassesAction.properties文件,
打开:WEB-INFlibstruts2-core-2.3.15.3.jar/org/apache/struts2/struts-messages.properties该文件,
复制
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
该句代码至ClassesAction.properties文件中,进行修改,如下图所示:
效果如下:
14.4、文件下载
struts.xml 的 result 中提供stream类型
inputName 设置 InputStream 获得属性值,需要提供getter方法
contentDisposition 设置 attachment;filename=${imageFileName} 获得文件名称