搭建SSM项目
-
1. 新建Maven工程,添加Web模板
-
2. 配置 pom.xml文件
-
3. 添加配置文件
-
4. 设置web.xml文件
-
5. 使用Mabatis逆向工程生成pojo和mapper
-
6. 添加MD5加密算法(保护密码)
-
7. 实现登录功能
-
8. 实现查询所有商品数据
-
9. 实现分页展示 –Ajax异步 — mybatis插件pagehelper
-
10 . 商品类别下拉选项实现 — 监听器实现(自动查询并存入全局对象中)
-
11. 上传文件 –ajax异步上传并回显 — springMVC组件
-
12 . 新增商品功能
-
13. 更新商品
-
14. ajax批量删除
1. 新建Maven工程,添加Web模板
创建Maven项目后没有webapp目录所以我们要添加web模块,
这里通过手动添加web模块,这种通过手动添加web模块方式创建的web项目是非常纯净的,没有任何附加的代码,开发中推荐。也可以使用maven的模板(在创建Maven时选择模板)创建web项目
如下图所示,添加web模块
2. 配置 pom.xml文件
2.1 集中定义全局变量 : 依赖版本号 (方便管理版本)
<!-- 集中定义依赖版本号 -->
<properties>
<junit.version>4.12</junit.version> <!-- 单元测试 -->
<spring.version>5.2.5.RELEASE</spring.version> <!-- spring -->
<mybatis.version>3.5.1</mybatis.version> <!-- mybatis -->
<mybatis.spring.version>1.3.1</mybatis.spring.version> <!-- spring、mybatis整合 -->
<mybatis.paginator.version>1.2.15</mybatis.paginator.version> <!-- mybatis支持的分页版本 -->
<mysql.version>8.0.22</mysql.version> <!-- mysql -->
<slf4j.version>1.6.4</slf4j.version> <!-- 日志 -->
<druid.version>1.1.12</druid.version> <!-- druid连接池 -->
<pagehelper.version>5.1.2</pagehelper.version> <!-- 分页的版本 -->
<jstl.version>1.2</jstl.version> <!-- jstl -->
<servlet-api.version>3.0.1</servlet-api.version> <!-- servlet-api -->
<jsp-api.version>2.0</jsp-api.version> <!-- jsp -->
<jackson.version>2.9.6</jackson.version> <!-- json工具 -->
</properties>
2.2 依赖
在
<dependencies> ... </dependencies>
中添加依赖
spring 所需要的依赖
<!-- spring -->
<!-- 上下文对象 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- bean工厂 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aop -->
<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>
<!-- test插件 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
Mybatis相关依赖
<!-- Mybatis -->
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis、spring整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!-- mybatis分页支持 -->
<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>
数据库相关的依赖
<!-- MySql -->
<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>
单元测试依赖
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
JSP相关依赖
<!-- JSP相关 -->
<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>
ackson Json处理工具包
<!-- Jackson Json处理工具包 -->
<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>
2.3 插件
在
<build> <plugins> ... </plugins></build>
中添加插件
jdk 编译版本
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
2.4 识别配置文件
在
<build> ... </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>
3. 添加配置文件
3.1 数据库连接信息jdbc.properties文件配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mimissm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
jdbc.username=root
jdbc.password=root
3.2 MyBatis核心配置文件mybatis-config.xml
mybatis大部分功能被spring接管,在这里分页pagehelper无法被spring接管,所以配置分页即可
<?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>
<!--分页插件的配置-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
3.3 Spring配置文件
对spring配置文件按照分层进行拆分
3.3.1 数据持久层 – applicationContext_dao.xml
其中实体类pojo和mapper文件夹未创建,后面通过mybatis逆向工程生成
<?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">
<!--读取jdbc配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--创建数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--创建SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!-- 配置mybatis核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--配置实体类-->
<property name="typeAliasesPackage" value="com.yanyu.pojo"></property>
</bean>
<!--扫描Mapper文件-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yanyu.mapper"/>
</bean>
</beans>
3.3.2 业务逻辑层 – applicationContext_service.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: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">
<!--设置业务逻辑层的包扫描器,目的是在指定的路径下,使用@Service注解的类,Spring负责创建对象,并添加依赖-->
<context:component-scan base-package="com.yanyu.service"/>
<!--设置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- dataSource在applicationContext_dao.xml中,最终配置文件都会注入spring容器中,所以不需引入 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--添加事务切面-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<!-- 声明事务管理策略 -->
<tx:attributes>
<!-- *select*:id中包含select的方法 read-only:只读,在读的时候其他方法不能进行修改 -->
<tx:method name="*select*" read-only="true"/>
<tx:method name="*find*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<tx:method name="*search*" read-only="true"/>
<!-- REQUIRED 如果上层方法没有事务,则创建一个新的事务;如果已经存在事务,则加入到事务中。-->
<tx:method name="*insert*" propagation="REQUIRED"/>
<tx:method name="*add*" propagation="REQUIRED"/>
<tx:method name="*delete*" propagation="REQUIRED"/>
<tx:method name="*remove*" propagation="REQUIRED"/>
<tx:method name="*clear*" propagation="REQUIRED"/>
<tx:method name="*update*" propagation="REQUIRED"/>
<tx:method name="*modify*" propagation="REQUIRED"/>
<tx:method name="*change*" propagation="REQUIRED"/>
<tx:method name="*set*" propagation="REQUIRED"/>
<!-- SUPPORTS 如果上层方法没有事务,则以非事务方式执行;如果已经存在事务,则加入到事务中。 -->
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--完成切面和切入点的植入-->
<aop:config>
<!--切入点-->
<aop:pointcut id="mypointcut" expression="execution(* com.yanyu.service.*.*(..))"/>
<!--将事务和切入点进行连接-->
<aop:advisor advice-ref="myadvice" pointcut-ref="mypointcut"/>
</aop:config>
</beans>
3.4 springMVC配置文件springmvc.xml
springmvc.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 设置包扫描器-->
<context:component-scan base-package="com.yanyu.controller"/>
<!-- 设置视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/admin/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 设置文件上传核心文件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
<!-- 设置注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
4. 设置web.xml文件
4.1 添加字符编码过滤器
注:字符编码过滤器需放在web.xml顶端
<?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">
<!--添加字符编码过滤器(注:字符编码过滤器需放在web.xml顶端)-->
<filter>
<filter-name>encode</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>forceRequestEncoding</param-name>
<param-value>ture</param-value>
</init-param>
<!--参数:强制响应转换-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>ture</param-value>
</init-param>
</filter>
<!--匹配所有请求-->
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--注册SpringMVC框架-->
<!--注册Spring框架-->
</web-app>
4.2 注册SpringMVC框架
<!--注册SpringMVC框架 - 核心是servlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--参数:springmvc配置文件路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<!--匹配以.action为后缀请求-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!--注册Spring框架-->
4.3 注册Spring框架
spring配置文件若在WEB-INF目录下,会自动加载,在其他位置要手动加载
加载多个文件时有两个方法
1. 逗号隔开 :classpath:applicationContext_dao.xml,classpath:applicationContext_service.xml
2. 使用通配符 :classpath:applicationContext_*.xml (代表以 applicationContext_ 为前缀以.xml为后缀的文件)
<!--注册Spring框架 - 通过监听器注册-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--spring配置文件若在WEB-INF目录下,会自动加载,在其他位置要手动加载-->
<!--加载spring配置文件(applicationContext_dao.xml,applicationContext_service.xml)-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--加载多个文件时有两个方法
1. 逗号隔开 :classpath:applicationContext_dao.xml,classpath:applicationContext_service.xml
2. 使用通配符 :classpath:applicationContext_*.xml (代表以 applicationContext_ 为前缀以.xml为后缀的文件)
-->
<param-value>classpath:applicationContext_*.xml</param-value>
</context-param>
5. 使用Mabatis逆向工程生成pojo和mapper
5.1 导入逆向工程
逆向工程简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sqI的定义需要我们手工编写
5.2 修改配置文件
在generatorConfig.xml中配置Mapper生成的详细信息,如下图:
注意修改内容主要以下几点:
-
修改数据库连接的信息
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mimissm?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" userId="root" password="root"> </jdbcConnection>
-
指定数据库表 (生成那些数据库表对应的文件) – admin,product_info,product_type
<!-- 指定数据库表 --> <table schema="" tableName="admin"></table> <table schema="" tableName="product_info"></table> <table schema="" tableName="product_type"></table>
-
生成PO类的位置 – com.yanyu.pojo
<!-- targetProject:生成PO类的位置 --> <javaModelGenerator targetPackage="com.yanyu.pojo" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator>
-
mapper映射文件生成的位置 – com.yanyu.mapper
<!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="com.yanyu.mapper" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator>
-
mapper接口生成的位置
<!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.yanyu.mapper" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator>
5.3 运行GeneratorSqlmap,生成pojo和mapper文件
运行前删除src下同名目录,防止文件重叠
运行程序后,在设置的目录下生成对应文件
5.4 MyBatis逆向工程中的Mapper接口以及Example的实例函数及详解
参考:
https://blog.csdn.net/qq_44058265/article/details/120460879
6. 添加MD5加密算法(保护密码)
-
MD5(message-digest algorithm 5)信息摘要算法,
它的长度一般是32位的16进制数字符串(如81dc9bdb52d04dc20036dbd8313ed055) - 由于系统密码明文存储容易被黑客盗取
-
应用:注册时,将密码进行md5加密,存到数据库中,防止可以看到数据库数据的人恶意篡改。
登录时,将密码进行md5加密,与存储在数据库中加密过的密码进行比对 -
md5不可逆,即没有对应的算法,从产生的md5值逆向得到原始数据。
但是可以使用暴力破解,这里的破解并非把摘要还原成原始数据,如暴力枚举法。
MD5Util工具类
package com.yanyu.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) //如果有小于0的字节,则转换为正数
temp =256+mdBytes[i];
else
temp=mdBytes[i];
if(temp<16)
hash+= "0";
hash+=Integer.toString(temp,16); //将字节转换为16进制后,转换为字符串
}
return hash;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
测试
@Test
public void testMD5(){
String md5 = MD5Util.getMD5("000000");
System.out.println(md5); //c984aed014aec7623a54f0591da07a85fd4b762d
}
7. 实现登录功能
7.1 业务逻辑层 – 创建AdminService接口,添加登录login
AdminService
package com.yanyu.service;
import com.yanyu.pojo.Admin;
public interface AdminService {
Admin login(String name,String pwd);
}
7.2 业务逻辑层 – 创建AdminServiceImpl,实现AdminService接口登录login
通过数据访问层对象AdminMapper,查找数据,成功返回用户数据,失败返回null
AdminServiceImpl
package com.yanyu.service.impl;
import ...
@Service
public class AdminServiceImpl implements AdminService {
//数据访问层对象,spring自动创建注入
@Autowired
AdminMapper adminMapper;
@Override
public Admin login(String name, String pwd) {
//根据用户名在数据库查找用户
//使用AdminExample对象来封装条件
AdminExample example = new AdminExample();
/* select * from admin where a_name = 'admin' */
//添加用户名a_name = 'admin'条件
example.createCriteria().andANameEqualTo(name);
//执行查询
List<Admin> adminList = adminMapper.selectByExample(example);
if(adminList.size()>0){
Admin admin = adminList.get(0);
//进行密码对比判断 - 密码是密文
String md5 = MD5Util.getMD5(pwd);
/* System.out.println(admin.getaPass()+" "+md5);*/
if(md5.equals(admin.getaPass())){
return admin;
}
}
return null;
}
}
7.3 界面控制层 – 创建AdminAtion,添加login,判断是否登陆成功,并跳转相应页面
调用业务逻辑层对象AdminServiceImpl,获取登录信息,登陆成功,跳转到main.jsp页面,登陆失败,跳转到login.jsp页面
AdminAtion
package com.yanyu.controller;
import ...
@Controller
@RequestMapping("/admin")
public class AdminAction {
//业务逻辑层对象
@Autowired
AdminService adminService;
//实现登陆的判断,进行相应跳转、
@RequestMapping("/login")
public String login(String name, String pwd, HttpServletRequest request){
Admin admin = adminService.login(name,pwd);
if(admin!=null){
//登陆成功,跳转到main.jsp页面(视图解析器)
request.setAttribute("admin",admin); // 存储用户信息
return "main";
}else {
//登陆失败,跳转到login.jsp页面(视图解析器)
request.setAttribute("errmsg","用户名或密码不正确!"); //返回失败信息
return "login";
}
}
}
8. 实现查询所有商品数据
8.1 业务逻辑层 – 创建ProductInfoService接口,添加查询所有商品功能getAll
ProductInfoService
package com.yanyu.service;
import ...
public interface ProductInfoService {
//显示所有商品、
List<ProductInfo> getAll();
}
8.2 业务逻辑层 – 创建ProductInfoServiceImpl,实现getAll
ProductInfoServiceImpl
package com.yanyu.service.impl;
import ...
@Service
public class ProductInfoServiceImpl implements ProductInfoService {
//数据访问层对象
@Autowired
ProductInfoMapper productInfoMapper;
@Override
public List<ProductInfo> getAll() {
//没有条件,查询所有
return productInfoMapper.selectByExample(new ProductInfoExample());
}
}
8.3 界面控制层 – 创建ProductInfoAtion,添加getAll,传递数据到前台
package com.yanyu.controller;
import ...
@Controller
@RequestMapping("/prod")
public class ProductInfoAction {
//业务逻辑层对象
@Autowired
ProductInfoService productInfoService;
//显示所有商品
@RequestMapping("/getAll")
public String getAll(HttpServletRequest request){
List<ProductInfo> list = productInfoService.getAll();
//传递数据
request.setAttribute("list",list);
//跳转页面
return "product";
}
}
9. 实现分页展示 –Ajax异步 – mybatis插件pagehelper
9.1 业务逻辑层 – ProductInfoService接口中,添加分页功能splitPage
PageInfo由分页插件pagehelper提供(PageInfo页面数据类,包含当前页,页大小,当前页大小,当前页数据…),pageNum当前页,pageSize页的大小
//分页功能,PageInfo由分页插件pagehelper提供,pageNum当前页,pageSize页的大小
PageInfo splitPage(int pageNum,int pageSize);
9.2 业务逻辑层 – ProductInfoServiceImpl中,实现splitPage,完成分页
@Override
public PageInfo splitPage(int pageNum, int pageSize) {
//分页插件使用PageHelper工具类完成分页设置(),放在取数据集合之前
PageHelper.startPage(pageNum,pageSize);
//条件查询 - 主键降序 select* from product_info order by p_id desc
ProductInfoExample example =new ProductInfoExample();
//添加条件order by p_id desc
example.setOrderByClause("p_id desc");
//查询数据,分页设置PageHelper要放在取数据集合之前
List<ProductInfo> list = productInfoMapper.selectByExample(example);
//将数据封装进PageInfo中,PageInfo自动对list进行分页
PageInfo<ProductInfo> pageInfo = new PageInfo<>(list);
return pageInfo;
}
9.3 界面控制层 – ProductInfoAtion中,添加splitPage,传递数据到前台product.jsp
显示初始页数据(第一页的5条数据)到前台 , 翻页由ajax异步来完成
设置每页显示记录数常量PAGE_SIZE,方便修改
//每页显示记录数
public static final int PAGE_SIZE = 5 ;
//显示初始页数据(第一页的5条数据)
@RequestMapping("/split")
public String split(HttpServletRequest request){
//得到第一页数据
PageInfo info = productInfoService.splitPage(1,PAGE_SIZE);
request.setAttribute("info",info);
return "product";
}
9.4 product.jsp前台接收数据展示,设置分页栏
前台接收初始页数据,通过ajax翻页,ajax传到界面控制层 – ProductInfoAtion中进行分页
<div id="table">
<!--info后台传来的PageInfo页面数据-->
<c:choose>
<!--info.list.size()页面数据大于0-->
<c:when test="${info.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()">
</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>
<!--从info中获取数据-->
<c:forEach items="${info.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>
<button type="button" class="btn btn-info "
onclick="one(${p.pId},${info.pageNum})">编辑
</button>
<button type="button" class="btn btn-warning" id="mydel"
onclick="del(${p.pId})">删除
</button>
</td>
</tr>
</c:forEach>
</table>
<!--分页栏-->
<div id="bottom">
<div>
<nav aria-label="..." style="text-align:center;">
<ul class="pagination">
<!--利用运用ajax异步翻页 上一页-->
<li>
<a href="javascript:ajaxsplit(${info.prePage})" aria-label="Previous">
<span aria-hidden="true">«</span></a>
<!--利用运用ajax异步翻页 对应页码-->
</li>
<!--当前页加深颜色-->
<c:forEach begin="1" end="${info.pages}" var="i">
<c:if test="${info.pageNum==i}">
<li>
<a href="javascript:ajaxsplit(${i})"
style="background-color: grey">${i}</a>
</li>
</c:if>
<c:if test="${info.pageNum!=i}">
<li>
<a href="javascript:ajaxsplit(${i})">${i}</a>
</li>
</c:if>
</c:forEach>
<!--利用运用ajax异步翻页 下一页-->
<li>
<a href="javascript:ajaxsplit(${info.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;">${info.pages}</font> 页
<!--当前页-->
<c:if test="${info.pageNum!=0}">
当前 <font style="color:orange;">${info.pageNum}</font> 页
</c:if>
<!-- PageInfo的最后一页下一页是0页,手动设置为一页-->
<c:if test="${info.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>
9.5 分页的AJAX实现
<!--分页的AJAX实现-->
<script type="text/javascript">
function ajaxsplit(page) {
//异步ajax分页请求
$.ajax({
url:"${pageContext.request.contextPath}/prod/ajaxSplit.action",
data:{"page":page},
type:"post",
success:function () {
//重新加载分页显示的组件table
//location.href---->http://localhost:8080/admin/login.action
$("#table").load("http://localhost:8080/admin/product.jsp #table");
}
})
};
</script>
9.6 界面控制层 – ProductInfoAtion中,添加ajaxSplit,对ajax进行解析完成分页
//ajax分页处理
//解析ajax请求,返回客户端
@ResponseBody
@RequestMapping("/ajaxSplit")
public void ajaxSplit(int page, HttpSession session){
//取得当前页page的数据
PageInfo info = productInfoService.splitPage(page,PAGE_SIZE);
System.out.println(info.getList());
session.setAttribute("info",info);
}
10 . 商品类别下拉选项实现 – 监听器实现(自动查询并存入全局对象中)
10.1 业务逻辑层 – 实现获取所有类别数据
创建ProductTypeService接口,定义获取所有类别数据方法getAll
package com.yanyu.service;
package ...
public interface ProductTypeService {
List<ProductType> getAll();
}
创建ProductTypeServiceImpl实现接口的所有类别数据方法getAll
package com.yanyu.service.impl;
import ...
@Service("ProductTypeServiceImpl")
public class ProductTypeServiceImpl implements ProductTypeService {
//数据访问层对象
@Autowired
ProductTypeMapper productTypeMapper;
@Override
public List<ProductType> getAll() {
return productTypeMapper.selectByExample(new ProductTypeExample());
}
}
10.2 通过监听器自动查询并存入全局对象中
spring容器的注册是通过监听器,与该对象是同一个监听器,无法确定谁先创建,所以无法使用spring的自动装配,需手动从Spring容器取出ProductTypeServiceImpl对象
package com.yanyu.listener;
import ...
@WebListener
public class ProductTypeListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//手动从Spring容器取出ProductTypeServiceImpl对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_*.xml");
ProductTypeService productTypeService = (ProductTypeService) context.getBean("ProductTypeServiceImpl");
List<ProductType> typeList = productTypeService.getAll();
//放入全局作用域中
sce.getServletContext().setAttribute("typeList",typeList);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
10.3 前端下拉框显示类别
<tr>
<td class="one">类别</td>
<td>
<select name="typeId">
<!--typeList后端传输的商品类别数据-->
<c:forEach items="${typeList}" var="type">
<option value="${type.typeId}">${type.typeName}</option>
</c:forEach>
</select>
</td>
</tr>
11. 上传文件 –ajax异步上传并回显 – springMVC组件
11.1 前端上传图片
文件选择
<tr>
<td class="three">图片介绍</td>
<!--ajax接收返回的图片位置并加载到imgDiv中-->
<td> <br><div id="imgDiv" style="display:block; width: 40px; height: 50px;"></div><br><br><br><br>
<!--选择文件,触发点击事件将文件传给Ajax异步上传-->
<input type="file" id="pimage" name="pimage" onchange="fileChange()" >
<span id="imgName" ></span><br>
</td>
Ajax异步提交事件 – 采用ajaxfileupload.js文件(封装ajax异步上传的功能)
<!-- 引入ajaxfileupload.js-->
<script type="text/javascript" src="${pageContext.request.contextPath }/js/ajaxfileupload.js"></script>
<script type="text/javascript">
function fileChange(){//注意:此处不能使用jQuery中的change事件,因此仅触发一次,因此使用标签的:onchange属性
$.ajaxFileUpload({
url: '/prod/ajaxImg.action',//用于文件上传的服务器端请求地址
secureuri: false,//是否需要安全协议一般设置为false
fileElementId: 'pimage',//文件上传控件的id属性 <input type="file" id="pimage" name="pimage" />
dataType: 'json',//返回值类型 一般设置为json
success: function(obj) //服务器成功响应处理函数
{
$("#imgDiv").empty(); //清空原有数据
//创建img 标签对象
var imgObj = $("<img>");
//给img标签对象追加属性
imgObj.attr("src","/image_big/"+obj.imgurl);
imgObj.attr("width","100px");
imgObj.attr("height","100px");
//将图片img标签追加到imgDiv末尾
$("#imgDiv").append(imgObj);
},
error: function (e)//服务器响应失败处理函数
{
alert(e.message);
}
});
}
</script>
11.2 在ProductInfoAction中添加异步Ajax文件上传处理
Ajax文件上传处理并返回含图片位置的json对象用于回显图片
//异步Ajax文件上传处理
@ResponseBody
@RequestMapping("/ajaxImg")
//pimage与前台上传的name值一样
public Object ajaxImg(MultipartFile pimage,HttpServletRequest request){
//生成文件名和后缀,通过FileNameUtil工具类将前台上传的文件名通过UUID重新生成,防止重复
String uuidFileName = FileNameUtil.getUUIDFileName()+FileNameUtil.getFileType(pimage.getOriginalFilename());
//存取路径 -- 完整的项目本地路径
String path = request.getServletContext().getRealPath("/image_big");
//存储 File.separator -> \
try {
pimage.transferTo(new File(path+File.separator+uuidFileName));
} catch (IOException e) {
e.printStackTrace();
}
//返回josn对象,包含图片路径,
String s = "{\"imgurl\":\""+uuidFileName+"\"}";
return s;
}
FileNameUtil工具类 – 用于UUID文件名防止重复
package com.yanyu.utils;
import java.util.UUID;
public class FileNameUtil {
//根据UUID生成文件名
public static String getUUIDFileName() {
UUID uuid = UUID.randomUUID();
return uuid.toString().replace("-", "");
}
//从请求头中提取文件名和类型
public static String getRealFileName(String context) {
// Content-Disposition: form-data; name="myfile"; filename="a_left.jpg"
int index = context.lastIndexOf("=");
String filename = context.substring(index + 2, context.length() - 1);
return filename;
}
//根据给定的文件名和后缀截取文件名
public static String getFileType(String fileName){
//9527s.jpg
int index = fileName.lastIndexOf(".");
return fileName.substring(index);
}
}
12 . 新增商品功能
12.1 业务逻辑层 – 实现新增商品
ProductInfoService接口添加save方法
//新增商品
int save(ProductInfo info);
ProductInfoServiceImpl实现类实现save
@Override
public int save(ProductInfo info) {
return productInfoMapper.insert(info);
}
12.2 界面控制层 – ProductInfoAtion中,添加save完成添加,返回初始页
@RequestMapping("/save")
public String save(ProductInfo info,HttpServletRequest request){
//文件由ajaxImg方法上传,将uuidFileName设置为全局变量,在此传递给数据库
info.setpImage(uuidFileName);
//前端提交的数据不包含时间,由后端添加
info.setpDate(new Date());
//num受影响行数
int num=0;
try {
num = productInfoService.save(info);
} catch (Exception e) {
e.printStackTrace();
}
if(num>0){
request.setAttribute("msg","增加成功!");
}else{
request.setAttribute("msg","增加失败!");
}
//转发到所有商品初始页
return "forward:/prod/split.action";
}
13. 更新商品
13.1 根据主键id查询商品,用于修改数据 – getById方法
业务逻辑层 – ProductInfoService接口添加getById方法
//按主键id查询商品
ProductInfo getById(int pid);
业务逻辑层 – ProductInfoServiceImpl实现类实现getById
@Override
public ProductInfo getById(int pid) {
return productInfoMapper.selectByPrimaryKey(pid);
}
界面控制层 – ProductInfoAtion中,添加one方法完成查询,返回修改页
@RequestMapping("one")
public String one(int pid, Model model){
ProductInfo byId = productInfoService.getById(pid);
model.addAttribute("prod",byId);
//更新时可以修改图片,防止数据冲突,清除uuidFileName
uuidFileName="";
return "update";
}
13.3 前端点击编辑,跳转one方法根据主键id查询所要修改的数据,回显
function one(pid) {
location.href = "${pageContext.request.contextPath}/prod/one.action?pid=" + pid;
}
....
<button type="button" class="btn btn-info " onclick="one(${p.pId})">编辑 </button>
13.3 更新前端页面 – 主要代码
//取消时返回初始页
<script type="text/javascript">
function myclose(ispage) { window.location="${pageContext.request.contextPath}/admin/product/split.action";
//window.close();
}
</script>
<div id="table">
<form action="${pageContext.request.contextPath}/prod/update.action" enctype="multipart/form-data" method="post" id="myform">
//修改根据id,所以要提交,但不修改用隐藏域
<input type="hidden" value="${prod.pId}" name="pId">
//用于判断是否重新上传图片
<input type="hidden" value="${prod.pImage}" name="pImage">
...
商品名称
商品介绍
定价
...
//显示图片和异步上传
<tr>
<td class="one">图片介绍</td>
<td> <br><div id="imgDiv" style="display:block; width: 40px; height: 50px;"><img src="/image_big/${prod.pImage}" width="100px" height="100px" ></div><br><br><br><br>
<input type="file" id="pimage" name="pimage" onchange="fileChange()">
<span id="imgName"></span><br>
</td>
</tr>
...
总数量
...
//取出全局中的数据,与修改商品的类别id对比,selected="selected"表示选中
<tr>
<td class="one">类别</td>
<td>
<select name="typeId">
<c:forEach items="${typeList}" var="type">
<option value="${type.typeId}"
<c:if test="${type.typeId==prod.typeId}">
selected="selected"
</c:if>
>${type.typeName}</option>
</c:forEach>
</select>
</td>
</tr>
<td>
<input type="submit" value="提交" class="btn btn-success">
</td>
<td>
<input type="reset" value="取消" class="btn btn-default" onclick="myclose(1)">
</td>
</tr>
</table>
</form>
</div>
13.4 修改数据 – update方法
业务逻辑层 – ProductInfoService接口添加update方法
//更新商品
int update(ProductInfo info);
业务逻辑层 – ProductInfoServiceImpl实现类实现update
@Override
public int update(ProductInfo info) {
return productInfoMapper.updateByPrimaryKey(info);
}
界面控制层 – ProductInfoAtion中,添加update完成更新
@RequestMapping("/update")
public String update(ProductInfo info,HttpServletRequest request){
//判断是否有重新上传的图片,若有则修改图片地址,没有不变
if(!uuidFileName.equals("")) {
info.setpImage(uuidFileName);
}
//num受影响行数
int num=0;
try {
num = productInfoService.update(info);
} catch (Exception e) {
e.printStackTrace();
}
if(num>0){
request.setAttribute("msg","更新成功!");
}else{
request.setAttribute("msg","更新失败!");
}
//转发到所有商品初始页
return "forward:/prod/split.action";
}
14. ajax批量删除
14.1 前端点击触发
//批量删除
function deleteBatch() {
//取得所有被选中删除商品的pid
var cks=$("input[name=ck]:checked");
//判断是否有选中的商品
if(cks.length==0){
// 没有选中的商品
alert("请选择将要删除的商品!");
}else{
var str="";
var id="";
// 有选中的商品,则取出每个选中商品的ID,拼接提交的ID的数据
if(confirm("您确定删除"+cks.length+"条商品吗?")){
//拼接ID
$.each(cks,function () {
id=$(this).val(); //22 33
//非空判断,防止出错
if(id!=null)
str += id+","; //22,33,44
});
//发送ajax请求
$.ajax({
url:"${pageContext.request.contextPath}/prod/deleteBatch.action",
data:{"ids":str},
type:"post",
dataType: "text",
success:function (msg) {
alert(msg);
$("#table").load("http://localhost:8080/admin/product.jsp #table");
}
});
}
}
}
14.2 在mapper中添加操作语句(复杂操作)
<!--批量删除-->
<delete id="deleteBatch">
delete from product_info where p_id in
/* 采用foreach来拼接多个数据
* collection="array" 数组类型
* separator="," 每个数值之间的间隔符
* open="(" 开始的字符
* close=")" 结束的字符
*/
<foreach collection="array" item="pid" separator="," open="(" close=")">
#{pid}
</foreach>
</delete>
14.3 实现删除
@Override
public int deleteBatch(String[] ids) {
return productInfoMapper.deleteBatch(ids);
}
//ajax异步批量删除
@ResponseBody
@RequestMapping(value = "/deleteBatch",produces = "text/html;charset=UTF-8")
public Object delete(String ids,HttpSession session){
//将上传的id拼接字符串拆分为字符数组
String[] pids = ids.split(",");
//num受影响行数
int num=0;
try {
num = productInfoService.deleteBatch(pids);
} catch (Exception e) {
e.printStackTrace();
}
//重新分页
PageInfo info = productInfoService.splitPage(1,PAGE_SIZE);
session.setAttribute("info",info);
if(num>0){
return "批量删除成功!";
}else{
return "批量删除失败!";
}
}