ssm项目(商城管理系统)-- 完整

搭建SSM项目

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生成的详细信息,如下图:
在这里插入图片描述
注意修改内容主要以下几点:

  1. 修改数据库连接的信息

    	<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
    	<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
    		connectionURL="jdbc:mysql://localhost:3306/mimissm?useSSL=false&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true" 
    		userId="root"
    		password="root">
    	</jdbcConnection>
    
  2. 指定数据库表 (生成那些数据库表对应的文件) – admin,product_info,product_type

     	<!-- 指定数据库表 -->
     	<table schema="" tableName="admin"></table>
     	<table schema="" tableName="product_info"></table>
     	<table schema="" tableName="product_type"></table>
    
  3. 生成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>
    
  4. mapper映射文件生成的位置 – com.yanyu.mapper

     <!-- targetProject:mapper映射文件生成的位置 -->
    	<sqlMapGenerator targetPackage="com.yanyu.mapper"
    		targetProject=".\src">
    		<!-- enableSubPackages:是否让schema作为包的后缀 -->
    		<property name="enableSubPackages" value="false" />
    	</sqlMapGenerator>
    
  5. 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加密算法(保护密码)

  1. MD5(message-digest algorithm 5)信息摘要算法,
    它的长度一般是32位的16进制数字符串(如81dc9bdb52d04dc20036dbd8313ed055)
  2. 由于系统密码明文存储容易被黑客盗取
  3. 应用:注册时,将密码进行md5加密,存到数据库中,防止可以看到数据库数据的人恶意篡改。
    登录时,将密码进行md5加密,与存储在数据库中加密过的密码进行比对
  4. 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">&nbsp;&nbsp;全选
                <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;">总共&nbsp;&nbsp;&nbsp;<font style="color:orange;">${info.pages}</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                     <!--当前页-->
                                    <c:if test="${info.pageNum!=0}">
                                        当前&nbsp;&nbsp;&nbsp;<font style="color:orange;">${info.pageNum}</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                    </c:if>
                                     <!-- PageInfo的最后一页下一页是0页,手动设置为一页-->
                                    <c:if test="${info.pageNum==0}"> 当前&nbsp;&nbsp;&nbsp;<font style="color:orange;">1</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                    </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  "批量删除失败!";
        }

    }

猜你喜欢

转载自blog.csdn.net/qq_44058265/article/details/120434698
今日推荐