一、项目名称
本项目主要目的是使学员更深层的了解IT企业的文化和岗位需求、模拟企业的工作场景,分享研制成果,增加学员对今后工作岗位及计算机应用开发对客观世界影响的感性认识,使学员对技术有更深入的理解,在今后工作中能有更明确的目标和方向。并能为日后职业规划提供很好的指导作用。
二、使用技术
- 服务端:Spring+SpringMVC+MyBatis框架整合
- 数据库:MySql8.0
- web服务器:Tomcat
- 项目管理:Maven
- 前端:jQuery + BootStrap + JavaScript
- 开发工具:idea2021
三、开发步骤
- 介绍项目实现功能,使用到的技术点,使用到的框架,演示最终效果。
- 创建数据库和表,添加idea可视化。
- 完成SSM框架的搭建。
- 添加字符编码过滤器,实现中文编码解析。
- 使用数据库连接池技术,使用mybatis框架完成底层实体类的创建和数据访问层的实现。
- 实现登录功能。
- 实现登录成功后全部商品分页显示。
- 实现Ajax的翻页功能。
- 添加监听器,实现获取全部商品类型功能。
- 实现商品增加功能。
- 实现Ajax商品删除功能。
- 实现Ajax商品批量删除功能。
- 实现商品修改的回显功能。
- 实现商品修改功能。
- 实现按商品名称条件查询并分页功能,翻页使用Ajax技术。
- 实现按商品类型查询并分页功能,翻页使用Ajax技术。
- 实现按商品价格查询并分页功能,翻页使用Ajax技术。
- 实现按商品名称、商品类型、商品价格多条件查询并分页,翻页使用Ajax技术。
- 实现退出功能。
四、具体实现
1、创建数据库表
DROP DATABASE IF EXISTS xiaomissm;
CREATE DATABASE xiaomissm DEFAULT CHARSET utf8;
USE xiaomissm;
DROP TABLE IF EXISTS `orderdetail`;
DROP TABLE IF EXISTS `xmorder`;
DROP TABLE IF EXISTS `carshop`;
DROP TABLE IF EXISTS `address`;
DROP TABLE IF EXISTS `users`;
drop table if exists product_info;
drop table if exists product_type;
drop table if exists admin;
CREATE TABLE admin(
a_id INT AUTO_INCREMENT PRIMARY KEY,
a_name VARCHAR(20),
a_pass VARCHAR(20)
);
INSERT INTO admin(a_id,a_name,a_pass) VALUES(1,'admin','000000');
CREATE TABLE product_type
(
type_id int auto_increment PRIMARY KEY,
type_name varchar(20)
);
insert into product_type(type_name) values('手机');
insert into product_type(type_name) values('电脑');
insert into product_type(type_name) values('电视');
create table product_info
(
p_id int auto_increment primary key,
p_name varchar(20),
p_content varchar(200),
p_price int,
p_image varchar(200),
p_number int,
type_id int,
p_date date,
FOREIGN KEY(type_id) REFERENCES product_type(type_id)
);
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米Note2','双曲面 黑色 6GB内存 64GB闪存',2899,'xmNote2.jpg',500,1,'2018-01-04');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('红米Note5A','5.5英寸 粉色 2GB内存 16GB闪存',699,'hmNote5A.jpg',500,1,'2018-01-05');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('红米Note4X','5.5英寸 绿色 4GB内存 64GB闪存',1299,'hmNote4X.jpg',500,1,'2018-01-06');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('红米4','5英寸 金色 3GB内存 32GB闪存',999,'hm4.jpg',500,1,'2018-01-07');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('红米4X','5英寸 黑色 3GB内存 32GB闪存',899,'hm4X.jpg',500,1,'2018-01-08');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米平板3','7.9英寸 金色 4GB内存 64GB闪存',1499,'xmPad3.jpg',500,2,'2018-01-09');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米Air12','12.5英寸 银色 4GB内存 128GB闪存',3599,'xmAir12.jpg',500,2,'2018-01-18');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米Air13','13.3英寸 银色 8GB内存 256GB闪存',4999,'xmAir13.jpg',500,2,'2018-01-17');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米Pro','15.6英寸 灰色 16GB内存 256GB闪存',6999,'xmPro.jpg',500,2,'2018-01-16');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米电视4','49英寸 原装LG屏 3840×2160 真4K',3299,'xmTV4-49.jpg',500,3,'2018-01-15');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米电视4','55英寸 原装三星屏 3840×2160 真4K',3999,'xmTV4-55.jpg',500,3,'2018-01-13');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米电视4','65英寸 原装三星屏 3840×2160 真4K',8999,'xmTV4-65.jpg',500,3,'2018-01-22');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米电视4A','43英寸 FHD全高清屏 1920*1080',1999,'xmTV4A-43.jpg',500,3,'2018-01-11');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米电视4A','49英寸 FHD全高清屏 1920*1080',2299,'xmTV4A-49.jpg',500,3,'2018-01-21');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米MIX2','全陶瓷 黑色 8GB内存 128GB闪存',4699,'xmMIX2.jpg',500,1,'2018-04-01');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米Note3','全网通 蓝色 6GB内存 64GB闪存',2499,'xmNote3.jpg',500,1,'2018-03-01');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米6','玻璃金属 白色 6GB内存 128GB闪存',2899,'xm6.jpg',500,1,'2018-02-01');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米MAX2','全金属 金色 4GB内存 64GB闪存',1599,'xmMAX2.jpg',500,1,'2018-01-02');
insert into product_info(p_name,p_content,p_price,p_image,p_number,type_id,p_date) values('小米5X','全金属 金色 4GB内存 64GB闪存',1499,'xm5X.jpg',500,1,'2018-01-03');
CREATE TABLE `users` (
`uid` int(11) NOT NULL auto_increment,
`uname` varchar(50) default NULL,
`upass` varbinary(50) default NULL,
`ustatus` int(11) default NULL,
`ulevel` int(11) default NULL,
`score` int(11) default NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `users` VALUES ('1', 'zar', 0x313233343536, '0', '0', '0');
INSERT INTO `users` VALUES ('2', 'zhangsan', 0x313233343536, '1', '0', '0');
CREATE TABLE `address` (
`addressId` int(11) NOT NULL auto_increment,
`uid` int(11) default NULL,
`cnee` varchar(50) default NULL,
`phone` varchar(11) default NULL,
`address` varchar(100) default NULL,
PRIMARY KEY (`addressId`),
KEY `FK_Reference_1` (`uid`),
CONSTRAINT `FK_Reference_1` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `address` VALUES ('1', '1', 'zar', '15266676667', '北京海淀甲骨文');
INSERT INTO `address` VALUES ('2', '1', 'oracle', '15266678888', '北京朝阳科技文化一条街');
INSERT INTO `address` VALUES ('3', '2', '张三', '15290888162', '北京大兴西红门');
CREATE TABLE `carshop` (
`cid` int(11) NOT NULL auto_increment,
`uid` int(11) default NULL,
`pid` int(11) default NULL,
`numbers` int(11) default NULL,
PRIMARY KEY (`cid`),
KEY `FK_Reference_3` (`uid`),
KEY `FK_Reference_4` (`pid`),
CONSTRAINT `FK_Reference_4` FOREIGN KEY (`pid`) REFERENCES `product_info` (`p_id`),
CONSTRAINT `FK_Reference_3` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into carshop (uid,pid,numbers) values (1,1,2);
CREATE TABLE `xmorder` (
`oid` char(32) NOT NULL ,
`uid` int(11) default NULL,
`addressId` int(11) default NULL,
`totalprice` double(10,2) default NULL,
`remarks` varchar(200) default NULL,
`status` varchar(6) default NULL,
`odate` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY (`oid`),
KEY `FK_Reference_5` (`uid`),
KEY `FK_Reference_6` (`addressId`),
CONSTRAINT `FK_Reference_6` FOREIGN KEY (`addressId`) REFERENCES `address` (`addressId`),
CONSTRAINT `FK_Reference_5` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into xmorder(oid,uid,addressid,totalprice,remarks,status,odate) values('abcd111222333444777888999000wwww',1,1,9996,'尽快送到','待发货',default);
CREATE TABLE `orderdetail` (
`odid` int(11) NOT NULL auto_increment,
`oid` char(32) default NULL,
`pid` int(11) default NULL,
`pnumber` int(11) default NULL,
`ptotal` double(10,2) default NULL,
PRIMARY KEY (`odid`),
KEY `FK_Reference_7` (`oid`),
KEY `FK_Reference_8` (`pid`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`pid`) REFERENCES `product_info` (`p_id`),
CONSTRAINT `FK_Reference_9` FOREIGN KEY (`oid`) REFERENCES `xmorder` (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into orderdetail(oid,pid,pnumber,ptotal) values ('abcd111222333444777888999000wwww',1,2,9996);
select * from admin;
select * from users;
select * from product_type;
select * from product_info ;
select * from orderdetail;
select * from xmorder;
select * from carshop;
select * from address;
用 Navicat 执行此 SQL文件 即可
2、项目结构
SSM 项目分层参考 : SSM框架下各个层的解释说明
3、配置文件
pom.xml
与之前不同整合项目不同的是,在此版本号进行集中管理,便于后续进行更改
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fancy</groupId>
<artifactId>xiaomissm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>xiaomissm Maven Webapp</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<junit.version>4.12</junit.version>
<spring.version>5.2.5.RELEASE</spring.version>
<mybatis.version>3.5.1</mybatis.version>
<mybatis.spring.version>1.3.1</mybatis.spring.version>
<mybatis.paginator.version>1.2.15</mybatis.paginator.version>
<mysql.version>8.0.22</mysql.version>
<slf4j.version>1.6.4</slf4j.version>
<druid.version>1.1.12</druid.version>
<pagehelper.version>5.1.2</pagehelper.version>
<jstl.version>1.2</jstl.version>
<servlet-api.version>3.0.1</servlet-api.version>
<jsp-api.version>2.0</jsp-api.version>
<jackson.version>2.9.6</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<dependency>
<groupId>com.github.miemiedev</groupId>
<artifactId>mybatis-paginator</artifactId>
<version>${mybatis.paginator.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
<version>${jsp-api.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<finalName>xiaomissm</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
db.properties
在此需要在 password、username等前加上前缀 jdbc. ,以防与登录界面中的username、password的相冲突
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xiaomissm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
jdbc.username=root
jdbc.password=123456
application-services.xml 配置文件中的 ${ } 可以取到全局域所有的username与password#
applicationContext-dao.xml
此文件主要完成 Spring 在 dao 包(持久层)相关配置,主要分为以下四步
(1) 配置属性文件 :读取数据库配置文件 db.properties ,引入连接数据库的相关属性
(2) 配置数据源 :数据库连接池用到阿里旗下 druid 连接池,读取数据库属性,完成数据库的连接
(3) 配置 MyBatis 工厂 : 需要用到数据源与mybatis核心配置文件
(4) 创建动态代理对象 :代码中有解释
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<property name="basePackage" value="com.fancy.dao"></property>
</bean>
</beans>
applicationContext-services.xml
此文件主要完成 Spring 在 services 包 (业务层) 相关配置,主要分为以下几步操作
(1) 配置组件扫描器,完成对于 services 包的扫描,使注解生效
(2) 设置事务管理器
(3) 添加事务切面
(4) 完成切面和切入点的织入
至于为什么要在 services 包而不是 dao 包下添加事务,可以参考此文章: https://blog.csdn.net/m0_51111980/article/details/123140072?spm=1001.2014.3001.5501
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="classpath:applicationContext-dao.xml"></import>
<context:component-scan base-package="com.fancy.services"></context:component-scan>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="select*" read-only="true"/>
<tx:method name="insert*" propagation="REQUIRED"></tx:method>
<tx:method name="delete*" propagation="REQUIRED"></tx:method>
<tx:method name="update*" propagation="REQUIRED"></tx:method>
<tx:method name="*" propagation="SUPPORTS"></tx:method>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* com.fancy.services.*.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>
</beans>
springmvc.xml
springmvc.xml 配置文件主要完成 Spring 在 controllers 包(控制层) 需要的相关配置,分为以下四步
(1) 注册组件扫描器:使 controllers 包下注解生效
(2) 指定视图解析器:简化视图路径,便于开发
(3) 设置文件上传核心组件:使用MultipartFile这个类主要是来实现以表单的形式进行多文件上传功能
(4) 注册注解驱动:<mvc:annotation-driven> 后,默认会帮我们注册默认处理请求,参数和返回值的类。其会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为 @Controller 分发请求所必须的。
<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.alibaba.com/schema/stat"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd">
<context:component-scan base-package="com.fancy.controllers"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
<mvc:annotation-driven/>
</beans>
SqlMapConfig.xml
SqlMapConfig.xml 作为 MyBatis 全局配置文件,一般完成两个设置别名、指定 sql 映射文件。由于我们要在项目中用到分页功能,在此我们要注册分页插件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.fancy.pojo"/>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<mappers>
<package name="com.fancy.dao"/>
</mappers>
</configuration>
web.xml
我们需要在 web.xml 配置文件中完成注册字符编码过滤器、注册Spring框架、注册 SpringMVC 框架的代码,以便在web项目启动时框架可以正常使用。
注册Spring框架主要完成 注册监听器和加载applicationContext.xml配置文件 两个部分
注册SpringMVC框架主要完成注册中央调度器和拦截匹配的请求两部分,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequsetEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
</web-app>
4、使用 MyBatis 逆向工程生成 pojo 类 和 dao 类
在 maven 中添加依赖
分别是mybatis-generator-maven-plugin插件,log4j、mybatis、mysql、mybatis-generator的依赖
依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
插件:
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
</dependencies>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
插件无法显示怎么办? 参考文章 https://blog.csdn.net/chenyao1994/article/details/85916642
同时需要在插件中再次加入 mysq-connector-java 依赖,否则在运行时会出现以下错误 配置成功后出现以下插件
配置逆向工程核心配置文件 generatorConfig.xml
根据下方注释进行配置即可
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/xiaomissm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
userId="root"
password="aszhuo123">
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<javaModelGenerator targetPackage="com.fancy.pojo" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="com.fancy.dao" targetProject="./src/main/java">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.fancy.dao" targetProject="./src/main/java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<table tableName="address"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="admin"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="carshop"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="orderdetail"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="product_info"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="product_type"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="users"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="xmorder"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
运行并生成 pojo类 和 dao类
鼠标移到插件,右键 Run Maven Build 生成代码如图
5、utils 包下添加 MD5 加密算法工具类
此算法主要在注册登录页面的表单提交数据时使用
package com.fancy.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public final static String getMD5(String str){
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(str.getBytes());
byte mdBytes[] = md.digest();
String hash = "";
for(int i= 0;i<mdBytes.length;i++){
int temp;
if(mdBytes[i]<0)
temp =256+mdBytes[i];
else
temp=mdBytes[i];
if(temp<16)
hash+= "0";
hash+=Integer.toString(temp,16);
}
return hash;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
6、登录功能
我们先编写登录界面 login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.css" />
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/login.css" />
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/bootstrap.js"></script>
<title></title>
</head>
<body>
<div id="login">
<div id="top">
<img src="${pageContext.request.contextPath}/images/cloud.jpg" /><span>LOGIN</span>
</div>
<div id="bottom">
<form action="${pageContext.request.contextPath}/admin/login.action" method="post">
<table border="0px" id="table">
<tr>
<td class="td1">用户名:</td>
<td><input type="text" placeholder="Username" class="td2" name="name"></td>
</tr>
<tr>
<td></td>
<td><span id="nameerr"></span></td>
</tr>
<tr>
<td class="td1">密码:</td>
<td><input type="password" placeholder="Password" class="td2" name="pwd"></td>
</tr>
<tr>
<td></td>
<td><span id="pwderr"></span></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="登录" class="td3">
<a href="${pageContext.request.contextPath}/regist.jsp"><input type="button" value="注册" class="td3 "></a>
</td>
</tr>
</table>
</form>
${errmsg}
</div>
</div>
</body>
</html>
如图所示 接下来是做登录判断,如果表单提交数据与数据库表中数据相符,则登录成功;反之,则登录失败。
做登录判断的功能函数属于业务层范围,我们在services包下定义 AdminService 接口及其实现类 AdminServiceImpl,其内部封装 login() 作为登录判断的函数。
做业务层开发时,我们知道业务层函数是基于持久层操作数据库方法的,所以业务层实现类一定会有持久层的对象。
既然要判断表中是否存在该数据,我们需要到 MyBatis 逆向工程生成的 AdminMapper 接口中寻找 根据用户名称查询 的方法。
我们发现其中只有根据主键查询数据的方法,而 admin 表中 aName属性不是主键,在此我们需要额外再创建方法
Admin selectByName(String aName) 来根据用户提交的username来查询
mapper 映射文件中同时定义
<select id="selectByName" parameterType="java.lang.String" resultType="BaseResultMap">
select
<include refid="Base_Column_List"></include>
from admin
where a_name = #{aName, jdbcType=VARCHAR}
</select>
仿照原有的方法 selectByPrimaryKey 进行定义
AdminService.java
package com.fancy.services;
import com.fancy.pojo.Admin;
public interface AdminService {
Admin login(String name, String pwd);
}
AdminServiceImpl.java
package com.fancy.services;
import com.fancy.dao.AdminMapper;
import com.fancy.pojo.Admin;
import com.fancy.utils.MD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AdminSeriviceImpl implements AdminService{
@Autowired
AdminMapper adminMapper;
@Override
public Admin login(String name, String pwd) {
Admin admin = adminMapper.selectByName(name);
if(admin != null) {
String mPwd = MD5Util.getMD5(pwd);
if (mPwd.equals(admin.getaPass())) {
return admin;
}
}
return null;
}
}
从数据库中取出数据后,我们需要与前端提交的数据进行比对。接收浏览器数据属于控制层范畴,此时比对的操作需要在控制层完成,然而我们也需要刚刚从数据库中获取的数据对象,所以我们又可以得到一个结论:控制层中一定有业务层对象。 AdminService 实现类 AdminServiceImpl 我们通过注解 @Service 已经创建好了对象 adminServiceImpl 我们在控制层AdminAction类中定义 AdminService adminService,上方加上 @Autowired 注解进行扫描,当扫描到adminServiceImpl 这个对象由于是其实现类对象,会自动进行注入。这种写法起到了解耦合的作用。
如不理解可以参考一下文章:https://blog.csdn.net/qq_32224047/article/details/107082167
package com.fancy.controllers;
import com.fancy.pojo.Admin;
import com.fancy.services.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/admin")
public class AdminAction {
@Autowired
AdminService adminService;
@RequestMapping("/login.action")
public String login(String name, String pwd, HttpServletRequest request) {
Admin admin = adminService.login(name, pwd);
if (admin != null) {
request.setAttribute("admin", admin);
return "main";
} else {
request.setAttribute("errmsg", "用户名或密码不正确");
return "login";
}
}
}
登录测试:
登录成功: 登录失败:
7、分页与翻页
步骤: 将全部商品不分页显示出来 -----> 分页显示 ------> 翻页功能
首先我们给出商品管理总界面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.css" />
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/index.css" />
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/bootstrap.js"></script>
<title></title>
<%--<link href="${pageContext.request.contextPath}/css/main.css" rel="stylesheet" >--%>
<style type="text/css">
</style>
</head>
<body>
<div id="all">
<div id="top">
<div id="top1">
<span>商品管理系统</span>
</div>
<div id="top2"></div>
<div id="top3">
<span>欢迎您,${name}</span>
</div>
</div>
<div id="bottom">
<div id="bleft">
<div id="ltop">
<div id="lts">
<img src="${pageContext.request.contextPath}/images/logo.jpg" /><br />
<p style="text-align: center;">随机访客</p>
</div>
</div>
<div id="lbottom">
<ul>
<a href="${pageContext.request.contextPath}/prod/split.action" target="myright" >
<li class="two"><span class="glyphicon glyphicon-book" style="color: white;"></span> 商品管理 <span class="glyphicon glyphicon-play" style="color: white;"></span> </li>
</a>
<a href="${pageContext.request.contextPath}/admin/err.jsp" target="myright">
<li class="one"><span class="glyphicon glyphicon-sort" style="color: white;"></span> 订单管理 <span class="glyphicon glyphicon-play" style="color: white;"></span> </li>
</a>
<a href="${pageContext.request.contextPath}/admin/err.jsp" target="myright">
<li class="one"><span class="glyphicon glyphicon-user" style="color: white;"></span> 用户管理 <span class="glyphicon glyphicon-play" style="color: white;"></span> </li>
</a>
<a href="${pageContext.request.contextPath}/admin/err.jsp" target="myright">
<li class="one"><span class="glyphicon glyphicon-bullhorn" style="color: white"></span> 通知公告 <span class="glyphicon glyphicon-play" style="color: white;"></span> </li>
</a>
</ul>
</div>
</div>
<div id="bright">
<iframe frameborder="0" scrolling="no" name="myright" width="1235px" height="700px" ></iframe>
</div>
</div>
</div>
</body>
</html>
同上一个实现登录的步骤,我们先开发 service 接口及其实现类,先编写 getAll() 方法以获取所有商品数据
然后我们在实现类 ProductInfoServiceImpl 中将 getAll() 方法进行实现,实际上是调用 mapper 类中 selectAll()方法,如没有请自行定义。
getAll() 方法的实现。 在控制层中,我们接受到浏览器的请求之后,调用通过业务层对象调用getAll()方法从数据库中获取所有商品的数据,添加到 request 作用域中,并提交给 Product.jsp 界面,在 Product.jsp 中所有商品的数据显现出来。
Product.jsp 界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page import="com.oracle.xiaomi.pojo.*" %>
<%@page import="java.util.*" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
if ("${msg}" != "") {
alert("${msg}");
}
</script>
<c:remove var="msg"></c:remove>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.css"/>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bright.css"/>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/addBook.css"/>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/bootstrap.js"></script>
<title></title>
</head>
<body>
<div id="brall">
<div id="nav">
<p>商品管理>商品列表</p>
</div>
<div id="condition" style="text-align: center">
<form id="myform">
商品名称:<input name="pname" id="pname">
商品类型:<select name="typeid" id="typeid">
<option value="-1">请选择</option>
<c:forEach items="${ptlist}" var="pt">
<option value="${pt.typeId}">${pt.typeName}</option>
</c:forEach>
</select>
价格:<input name="lprice" id="lprice">-<input name="hprice" id="hprice">
<input type="button" value="查询" onclick="ajaxsplit(${pb.pageNum})">
</form>
</div>
<br>
<div id="table">
<c:choose>
<c:when test="${pb.list.size()!=0}">
<div id="top">
<input type="checkbox" id="all" onclick="allClick()" style="margin-left: 50px"> 全选
<a href="${pageContext.request.contextPath}/admin/addproduct.jsp">
<input type="button" class="btn btn-warning" id="btn1"
value="新增商品">
</a>
<input type="button" class="btn btn-warning" id="btn1"
value="批量删除" onclick="deleteBatch(${pb.pageNum})">
</div>
<div id="middle">
<table class="table table-bordered table-striped">
<tr>
<th></th>
<th>商品名</th>
<th>商品介绍</th>
<th>定价(元)</th>
<th>商品图片</th>
<th>商品数量</th>
<th>操作</th>
</tr>
<c:forEach items="${list}" var="p">
<tr>
<td valign="center" align="center"><input type="checkbox" name="ck" id="ck" value="${p.pId}" onclick="ckClick()"></td>
<td>${p.pName}</td>
<td>${p.pContent}</td>
<td>${p.pPrice}</td>
<td><img width="55px" height="45px"
src="${pageContext.request.contextPath}/image_big/${p.pImage}"></td>
<td>${p.pNumber}</td>
<%--<td><a href="${pageContext.request.contextPath}/admin/product?flag=delete&pid=${p.pId}" onclick="return confirm('确定删除吗?')">删除</a>--%>
<%-- <a href="${pageContext.request.contextPath}/admin/product?flag=one&pid=${p.pId}">修改</a></td>--%>
<td>
<button type="button" class="btn btn-info "
onclick="one(${p.pId},${pb.pageNum})">编辑
</button>
<button type="button" class="btn btn-warning" id="mydel"
onclick="del(${p.pId},${pb.pageNum})">删除
</button>
</td>
</tr>
</c:forEach>
</table>
<div id="bottom">
<div>
<nav aria-label="..." style="text-align:center;">
<ul class="pagination">
<li>
<%-- <a href="${pageContext.request.contextPath}/prod/split.action?page=${pb.prePage}" aria-label="Previous">--%>
<a href="javascript:ajaxsplit(${pb.prePage})" aria-label="Previous">
<span aria-hidden="true">?</span></a>
</li>
<c:forEach begin="1" end="${pb.pages}" var="i">
<c:if test="${pb.pageNum==i}">
<li>
<%-- <a href="${pageContext.request.contextPath}/prod/split.action?page=${i}" style="background-color: grey">${i}</a>--%>
<a href="javascript:ajaxsplit(${i})"
style="background-color: grey">${i}</a>
</li>
</c:if>
<c:if test="${pb.pageNum!=i}">
<li>
<%-- <a href="${pageContext.request.contextPath}/prod/split.action?page=${i}">${i}</a>--%>
<a href="javascript:ajaxsplit(${i})">${i}</a>
</li>
</c:if>
</c:forEach>
<li>
<%-- <a href="${pageContext.request.contextPath}/prod/split.action?page=1" aria-label="Next">--%>
<a href="javascript:ajaxsplit(${pb.nextPage})" aria-label="Next">
<span aria-hidden="true">?</span></a>
</li>
<li style=" margin-left:150px;color: #0e90d2;height: 35px; line-height: 35px;">总共 <font
style="color:orange;">${pb.pages}</font> 页
<c:if test="${pb.pageNum!=0}">
当前 <font
style="color:orange;">${pb.pageNum}</font> 页
</c:if>
<c:if test="${pb.pageNum==0}">
当前 <font
style="color:orange;">1</font> 页
</c:if>
</li>
</ul>
</nav>
</div>
</div>
</div>
</c:when>
<c:otherwise>
<div>
<h2 style="width:1200px; text-align: center;color: orangered;margin-top: 100px">暂时没有符合条件的商品!</h2>
</div>
</c:otherwise>
</c:choose>
</div>
</div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">新增商品</h4>
</div>
<div class="modal-body" id="addTD">
<form action="${pageContext.request.contextPath}/admin/product?flag=save" enctype="multipart/form-data"
method="post" id="myform">
<table>
<tr>
<td class="one">商品名称</td>
<td><input type="text" name="pname" class="two" class="form-control"></td>
</tr>
<tr class="three">
<td class="four"></td>
<td><span id="pnameerr"></span></td>
</tr>
<tr>
<td class="one">商品介绍</td>
<td><input type="text" name="pcontent" class="two" class="form-control"></td>
</tr>
<tr class="three">
<td class="four"></td>
<td><span id="pcontenterr"></span></td>
</tr>
<tr>
<td class="one">定价</td>
<td><input type="number" name="pprice" class="two" class="form-control"></td>
</tr>
<tr class="three">
<td class="four"></td>
<td><span id="priceerr"></span></td>
</tr>
<tr>
<td class="one">图片介绍</td>
<td><input type="file" name="pimage" class="form-control"></td>
</tr>
<tr class="three">
<td class="four"></td>
<td><span></span></td>
</tr>
<tr>
<td class="one">总数量</td>
<td><input type="number" name="pnumber" class="two" class="form-control"></td>
</tr>
<tr class="three">
<td class="four"></td>
<td><span id="numerr"></span></td>
</tr>
<tr>
<td class="one">类别</td>
<td>
<select name="typeid" class="form-control">
<c:forEach items="${typeList}" var="type">
<option value="${type.typeId}">${type.typeName}</option>
</c:forEach>
</select>
</td>
</tr>
<tr class="three">
<td class="four"></td>
<td><span></span></td>
</tr>
<tr>
<td>
<input type="submit" class="btn btn-success" value="提交" class="btn btn-success">
</td>
<td>
<button type="button" class="btn btn-info" data-dismiss="modal">取消</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
结果如图:
所有商品显示在一页中。
接下来,我们需要完成的是分页操作。分页操作实际上就是要在函数中实现 select * from product_info limit 10,5 查询,在此我们使用 PageHelper 插件来辅助完成分页操作。
在 ProductService 接口中添加 splitPage 方法并在 ProductServiceImpl 进行实现
@Override
public PageInfo splitPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<ProductInfo> list = productInfoMapper.selectAll();
PageInfo<ProductInfo> pageInfo = new PageInfo<>(list);
return pageInfo;
}
在控制层中,我们只需要将 splitPage() 方法执行即可,并将结果提交到 product.jsp 页面中 结果如图,第一页的数据就显现出来了
不过,此方法只能得到第一页的数据,要想得到第2、3、4等页的数据,我们需要定义 ajax 进行翻页操作。
翻页事实上是修改 PageHelper.startPage(pageNum, pageSize) 的 pageNum 属性,在定义 ajax 请求时我们要特别地将页数 page 进行传入。
每次将 page 传入 ajaxSplit(page) 函数中,并将数据提交控制器给 ajaxSplit.action 中,新的page数据存入到session中,EL表达式${}的原理是每次刷新页面都重新从session中取出数据。
ajax请求:
<script type="text/javascript">
function ajaxsplit(page) {
$.ajax({
url: "${pageContext.request.contextPath}/prod/ajaxSplit.action",
data: {"page": page},
type: "post",
success: function () {
$("#table").load("http://localhost:8080/MyWeb/admin/product.jsp #table");
}
});
}
</script>
控制器方法:
@RequestMapping("/ajaxSplit.action")
@ResponseBody
public void ajaxSplit(int page, HttpSession session) {
PageInfo info = productInfoService.splitPage(page, 5);
session.setAttribute("info", info);
}
结果:
未完待续
|