技术栈:Java SSM
技术选型 | 版本 |
---|---|
数据库 | MySQL 8.0.18 |
JDK | 1.8.0 |
Spring | 5.2.1 |
Spring MVC | 5.2.1 |
mybatis | 3.2.4 |
mysql-connector-java | 8.0.18 |
dbcp/c3p0/druid | c3p0 |
功能:
商城项目:(小米商城:Business -to- Customer B2C)
用户:收货地址、用户扩展资料…
商品:商品图片,商品类型,商品套餐,售后服务…
元素都可以抽象成实体类型,就是对应了数据库中的表格。
逆向工程,就是通过数据库中已经存在的数据表,反向生成Java中的实体类(生成对应的ORM持久层代码)
代码生成器作为商城项目的一个工具组件存在。
我们这里使用maven构建项目,便于管理jar包。
引入逆向工程依赖:
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.18version>
dependency>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.4.0version>
dependency>
逆向工程的生成规则,就是描述数据库中的哪些表,生成对应的Java实体类,同时生成映射配置文件,这个生成规则就是一个普通的配置文件。
在项目的主目录中创建一个配置文件:/generator.xml
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/xiaomi?serverTimezone=PRC"
userId="root"
password="Root2019">
jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
javaTypeResolver>
<javaModelGenerator targetPackage="com.damu.xiaomi.entry"
targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
javaModelGenerator>
<sqlMapGenerator targetPackage="mapper"
targetProject="./src/main/resources">
<property name="enableSubPackages" value="true" />
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.damu.xiaomi.dao"
targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
javaClientGenerator>
<table tableName="consumer" domainObjectName="Consumer">table>
<table tableName="goods" domainObjectName="Goods">table>
<table tableName="goods_images" domainObjectName="GoodsImages">table>
<table tableName="goods_type" domainObjectName="GoodsType">table>
<table tableName="goods_service" domainObjectName="GoodsService">table>
<table tableName="goods_package" domainObjectName="GoodsPackage">table>
<table tableName="goods_configure" domainObjectName="GoodsConfigure">table>
<table tableName="goods_cart" domainObjectName="GoodsCart">table>
<table tableName="goods_order" domainObjectName="GoodsOrder">table>
<table tableName="goods_order_item" domainObjectName="GoodsOrderItem">table>
<table tableName="goods_shipping_address" domainObjectName="GoodsShippingAddress">table>
context>
generatorConfiguration>
通过配置文件指定的生成规则,自动构建实体类和数据访问类,我们可以参考官方文档来编写。
package com.damu.xiaomi.utils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class CodeGenerator {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("generator.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
Spring配置文件:src/main/resources/applicaationContext.xml
SpringMVC配置文件:src/main/resources/SpringMVC.xml
mybatis配置文件:src/main/resources/mybatis-config.xml
数据源配置文件:src/main/resources/db.properties
项目启动时,框架初始化,需要在web.xml中添加启动配置。
在项目中创建对应的空的配置文件,同时给项目添加web支持。
web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
listener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<servlet>
<servlet-name>springDispacherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springMVC.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>springDispacherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>characterEncodingfilter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
filter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>characterEncodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>hiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.3version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.3version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.4version>
dependency>
针对配置文件中的配置信息进行处理,可以参考官方文档进行配置
applicaationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.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">
<context:component-scan base-package="com.damu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:property-placeholder location="classpath*:db.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="minPoolSize" value="${jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
<property name="maxIdleTime" value="${jdbc.maxIdleTime}" />
<property name="initialPoolSize" value="${jdbc.initialPoolSize}" />
<property name="acquireIncrement" value="${jdbc.acquireIncrement}" />
<property name="idleConnectionTestPeriod"
value="${jdbc.idleConnectionTestPeriod}" />
<property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}" />
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml">
property>
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.damu.xiaomi.dao"/>
bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<aop:config>
<aop:pointcut expression="execution(* com.damu.xiaomi.service..*(..))" id="txPoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
aop:config>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" />
<tx:method name="get*" read-only="true"/>
tx:attributes>
tx:advice>
beans>
springMVC.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://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.damu" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views">property>
<property name="suffix" value=".jsp">property>
bean>
<mvc:default-servlet-handler />
<mvc:annotation-driven/>
beans>
mybatis-config.xml
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
settings>
<typeAliases>
<package name="com.damu.entry"/>
typeAliases>
configuration>
db.properties
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/xiaomi?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.password=Root2019
jdbc.minPoolSize=10
jdbc.maxPoolSize=20
jdbc.maxIdleTime=1800
jdbc.initialPoolSize=2
jdbc.acquireIncrement=2
jdbc.idleConnectionTestPeriod=1800
jdbc.acquireRetryAttempts=2
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.1.RELEASEversion>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
dependency>
package com.damu.test;
import com.damu.xiaomi.dao.ConsumerMapper;
import com.damu.xiaomi.entry.Consumer;
import com.damu.xiaomi.entry.ConsumerExample;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
//这是一个要加载Spring容器的测试
@RunWith(SpringJUnit4ClassRunner.class)
//加载Spring的配置文件
@ContextConfiguration(locations = {
"classpath:applicationContext.xml"})
public class DaoTest {
@Autowired
private ConsumerMapper consumerMapper;
@Test
public void testConsumerInsert() {
// 创建consumer对象
Consumer consumer = new Consumer("xiaozhang", "123");
// 将consumer对象增加到数据库中
consumerMapper.insertSelective(consumer);
System.out.println("数据insert执行完成");
}
@Test
public void testConsumerSelectById() {
Consumer consumer = consumerMapper.selectByPrimaryKey(1);
System.out.println(consumer);
}
@Test
public void testConsumerSelectByExample() {
// 创建一个查询条件【账号+密码】
ConsumerExample ce = new ConsumerExample();
ce.createCriteria().andUsernameEqualTo("xiaozhang").andPasswordEqualTo("123");
// 查询数据
List<Consumer> consumers = consumerMapper.selectByExample(ce);
consumers.forEach(consumer-> System.out.println(consumer));
}
}
从简单业务模型处理–>复杂业务模型操作(注册,登录)–>首页数据加载–>搜索–>购物车–>订单
首先创建用户相关业务处理类
package com.damu.xiaomi.service;
import com.damu.xiaomi.dao.ConsumerMapper;
import com.damu.xiaomi.entry.Consumer;
import com.damu.xiaomi.entry.ConsumerExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ConsumerService {
@Autowired
private ConsumerMapper consumerMapper;
public boolean findConsumerWithUsernameAndPassword(Consumer consumer) {
// 创建一个条件对象
ConsumerExample ce = new ConsumerExample();
ce.createCriteria().andUsernameEqualTo(consumer.getUsername()).andPasswordEqualTo(consumer.getPassword());
// 查询数据
List<Consumer> consumerList = consumerMapper.selectByExample(ce);
// 判断返回结果
return consumerList != null && consumerList.size() == 1;
}
}
其次创建用户相关业务访问接口/控制器
package com.damu.xiaomi.controller;
import com.damu.xiaomi.entry.Consumer;
import com.damu.xiaomi.service.ConsumerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private ConsumerService consumerService;
@PostMapping(value="/login/auth", produces = {
"application/json;charset=UTF-8"})
@ResponseBody // 序列化--> 类型转换--> jackson --> json
public ResponseMessage login(@RequestParam String username, @RequestParam String password) {
System.out.println("接受到请求:/consumer/login/auth");
System.out.println("账号:" + username + "; 密码:" + password);
Consumer consumer = new Consumer(username, password);
boolean result = consumerService.findConsumerWithUsernameAndPassword(consumer);
System.out.println("登录结果:" + result);
return result ? "success" : "error";
}
}
基于web业务的单元测试
package com.damu.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:applicationContext.xml", "classpath:springMVC.xml"})
@WebAppConfiguration
public class WebTest {
// 声明一个模拟请求的对象
private MockMvc mockMvc;
// 需要一个web容器
@Autowired
private WebApplicationContext context;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testLogin() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/consumer/login/auth").param("username", "xiaozhang").param("password", "123")).andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
}
关于数据接口,提供给用户端调用,并且返回符合标准预期的数据访问接口,通常会有如下的要求
package com.damu.xiaomi.utils;
import java.util.HashMap;
import java.util.Map;
public class ResponseMessage {
private String errorCode;
private String errorMsg;
private Map<String, Object> objectMap = new HashMap<>();
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public Map<String, Object> getObjectMap() {
return objectMap;
}
public void setObjectMap(Map<String, Object> objectMap) {
this.objectMap = objectMap;
}
public ResponseMessage addObject(String key, Object value) {
this.objectMap.put(key, value);
return this;
}
public static ResponseMessage success() {
ResponseMessage rm = new ResponseMessage();
rm.setErrorCode("100");
rm.setErrorMsg("处理成功");
return rm;
}
public static ResponseMessage error() {
ResponseMessage rm = new ResponseMessage();
rm.setErrorCode("200");
rm.setErrorMsg("处理失败");
return rm;
}
}
重构登陆业务
@PostMapping(value="/login/auth", produces = {
"application/json;charset=UTF-8"})
@ResponseBody // 序列化--> 类型转换--> jackson --> json
public ResponseMessage login(@RequestParam String username,@RequestParam String password) {
/*
重构:登录业务
1. 验证账号+密码
2. 登记用户 -- 会话跟踪【session】
*/
System.out.println("接受到请求:/consumer/login/auth");
System.out.println("账号:" + username + "; 密码:" + password);
Consumer consumer = new Consumer(username, password);
consumer = consumerService.findConsumerWithUsernameAndPassword(consumer);
System.out.println("登录结果:" + consumer);
return consumer != null ? ResponseMessage.success() : ResponseMessage.error();
}
对登录页面进行改动
<script src="js/jquery/jquery-3.4.1.js">script>
<script>
$(function() {
// 点击登录按钮时,发送ajax请求
$("#login-btn").click(function() {
// 发送ajax请求
$.ajax({
url: "/xiaomi/consumer/login/auth",
method: "post",
data: {
"username": $("#username").val(),
"password": $("#password").val()
},
success:function(response) {
console.log("请求发送成功");
console.log(response);
if(response.errorCode === "100") {
// 登录成功
alert("恭喜,用户登录成功!");
window.location = "/xiaomi/index.jsp";
} else {
// 登录失败
$("#error-username").text("账号或者密码有误,请重新登录").css({
"color": "red"});
}
},
error: function() {
console.log("请求发送失败..");
}
});
});
});
script>
在ConsumerService中添加注册方法
public String register(Consumer consumer) {
// 验证用户的账号是否存在
ConsumerExample ce = new ConsumerExample();
ce.createCriteria().andUsernameEqualTo(consumer.getUsername());
// 根据条件对象查询数据
List<Consumer> consumerList = consumerMapper.selectByExample(ce);
if (consumerList != null && consumerList.size() > 0) {
return "注册失败,账号已经存在,请使用其他账号注册";
}
// 添加用户
consumerMapper.insertSelective(consumer);
return "注册成功";
}
在控制层ConsumerController中添加注册方法
@PostMapping("/register")
@ResponseBody
public ResponseMessage register(@RequestParam String username, @RequestParam String password) {
// 创建用户对象
Consumer consumer = new Consumer(username, password);
// 注册用户
String result = consumerService.register(consumer);
// 判断结果
if (result.contains("注册成功")) {
return ResponseMessage.success();
}
return ResponseMessage.error().addObject("msg", result);
}
网页视图及功能完善
<script src="js/jquery/jquery-3.4.1.js">script>
<script>
$(function() {
// 点击注册按钮时,发送请求
$("#register-btn").click(function() {
// 发送Ajax请求
$.ajax({
url: "/xiaomi/consumer/register",
method: "POST",
data: {
"username": $("#username").val(),
"password": $("#password").val()
},
success: function(response) {
if(response.errorCode === "100") {
alert("Congratulations,账号注册成功,请登录吧");
window.location = "/xiaomi/login.jsp";
}else{
$("#errorMsg").text(response.objectMap.msg).css({
"color": "red"});
}
},
error: function() {
$("#errorMsg").text("请求迷路了,小二正在赶来的路上,请稍后再试..").css({
"color": "red"});
}
});
});
});
script>
首页中需要的数据,从后台获取并且添加
开发业务处理层
3. 商品类型:GoodsTypeService
4. 商品:GoodsShippingService
GoodsTypeService
package com.damu.xiaomi.service;
import com.damu.xiaomi.dao.GoodsTypeMapper;
import com.damu.xiaomi.entry.GoodsType;
import com.damu.xiaomi.entry.GoodsTypeExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class GoodsTypeService {
@Autowired
private GoodsTypeMapper goodsTypeMapper;
/**
* 查询一级商品类型
* @return 返回所有的一级商品类型
*/
public List<GoodsType> findTopLevel() {
GoodsTypeExample gte = new GoodsTypeExample();
gte.createCriteria().andPidIsNull();
return goodsTypeMapper.selectByExample(gte);
}
/**
* 根据一级类型,查询所有所属二级类型
* @return 返回类型列表
*/
public List<GoodsType> findSecondLevel(GoodsType top) {
GoodsTypeExample gte = new GoodsTypeExample();
gte.createCriteria().andPidEqualTo(top.getId());
return goodsTypeMapper.selectByExample(gte);
}
}
GoodsShippingService
package com.damu.xiaomi.service;
import com.damu.xiaomi.dao.GoodsMapper;
import com.damu.xiaomi.dao.GoodsTypeMapper;
import com.damu.xiaomi.entry.Goods;
import com.damu.xiaomi.entry.GoodsExample;
import com.damu.xiaomi.entry.GoodsType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class GoodsShippingService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsTypeService goodsTypeService;
/**
* 根据id查询商品
* @param id 商品编号
* @return 返回查询到的商品
*/
public Goods findGoodsWithId(Integer id) {
return goodsMapper.selectByPrimaryKey(id);
}
/**
* 根据商品类型查询商品
* @param goodsType 二级商品类型
* @return 属于指定商品类型的所有商品
*/
public List<Goods> findGoodsWithType(GoodsType goodsType) {
GoodsExample ge = new GoodsExample();
ge.createCriteria().andGoodsTypeIdEqualTo(goodsType.getId());
return goodsMapper.selectByExample(ge);
}
/**
* 根据一级类型查询商品数据
* @param goodstype 一级类型
* @return 返回所有商品
*/
public List<Goods> findGoodsWithToptype(GoodsType goodstype) {
// 根据一级类型查询所有所属二级类型
List<GoodsType> gt = goodsTypeService.findSecondLevel(goodstype);
// 查询所有二级类型下的所有商品
List<Goods> goodsList = new ArrayList<>();
for (GoodsType goodsType : gt) {
List<Goods> goods = this.findGoodsWithType(goodsType);
goodsList.addAll(goods);
}
return goodsList;
}
/**
* 根据名称模糊搜索商品数据
* @param name 商品名称
* @return 返回符合搜索条件的商品
*/
public List<Goods> searchGoodsWithName(String name) {
// 构建条件
GoodsExample ge = new GoodsExample();
ge.createCriteria().andNameLike("%" + name + "%");
return goodsMapper.selectByExample(ge);
}
}
首页数据加载完善
修改entry.goods
//商品对应的商品图片
private List<GoodsImages> goodsImages;
public List<GoodsImages> getGoodsImages() {
return goodsImages; }
public void setGoodsImages(List<GoodsImages> goodsImages) {
this.goodsImages = goodsImages; }
开发首页控制器:IndexController.java
package com.damu.xiaomi.controller;
import com.damu.xiaomi.entry.Goods;
import com.damu.xiaomi.entry.GoodsType;
import com.damu.xiaomi.service.GoodsShippingService;
import com.damu.xiaomi.service.GoodsTypeService;
import com.damu.xiaomi.utils.ResponseMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class IndexController {
@Autowired
private GoodsTypeService goodsTypeService;
@Autowired
private GoodsShippingService goodsShippingService;
@GetMapping("/initIndex")
@ResponseBody
public ResponseMessage initIndex() {
// 查询一级商品类型数据
List<GoodsType> topLevel = goodsTypeService.findTopLevel();
Map<Integer, List<Goods>> goodsMap = new HashMap<>();
// 遍历查询每个一级类型下的二级类型,按照 二级类型:商品类型
for (GoodsType goodsType: topLevel) {
// 查询所属一级类型下的所有二级类型
List<GoodsType> secondLevel = goodsTypeService.findSecondLevel(goodsType);
// 查询每个二级类型下的商品
for(GoodsType secondGoodsType: secondLevel) {
// 查询商品
List<Goods> goodsList = goodsShippingService.findGoodsWithType(secondGoodsType);
// 存储数据
goodsMap.put(secondGoodsType.getId(), goodsList);
}
}
// 返回首页需要的数据
return ResponseMessage.success().addObject("goodsTypeList", topLevel).addObject("goodsMap", goodsMap);
}
}
开发首页视图/web/index.jsp渲染商品展示
<script src="js/jquery/jquery-3.4.1.js">script>
<script>
$(function() {
// 直接发送请求获取数据
$.ajax({
url: "/xiaomi/initIndex",
method: "GET",
success: function(response) {
console.log(response);
// 获取到后台返回的一级商品类型数据
let $goodsTypes = response.objectMap.goodsTypeList;
// 遍历商品类型
for(let i = 0; i < $goodsTypes.length; i++) {
// 创建对应的标签
let $li = $("<li>").attr("id", $goodsTypes[i].id).text($goodsTypes[i].name);
let $span = $("<span>").append($("<em>").addClass("glyphicon glyphicon-menu-right"));
$li.append($span);
// 将数据加载到网页中ul菜单中
$("#bannel-type > ul").append($li);
}
// 加载楼层商品
// 获取小米手机商品
let $xiaomiphonelist = response.objectMap.goodsMap["10001"];
loadGoods($xiaomiphonelist, $("#xiaomiphone"));
// 获取红米手机商品
let $hongmiphonelist = response.objectMap.goodsMap["10002"];
loadGoods($hongmiphonelist, $("#hongmiphone"));
// 获取游戏手机商品
let $gamephonelist = response.objectMap.goodsMap["10003"];
loadGoods($gamephonelist, $("#gamephone"));
// 获取电视商品
let $tvlist = response.objectMap.goodsMap["20002"];
loadGoods($tvlist, $("#tv"));
},
error: function() {
console.log("请求迷路了....")
}
});
function loadGoods(goodslist, container) {
// 遍历商品列表,将商品添加到页面汇总
for(let i = 0; i < goodslist.length; i++) {
let $boxitem = $("<div>").addClass("goodsboxitem");
let $a = $("<a>").attr("href", "/xiaomi/detail.jsp?gid=" + goodslist[i].id);
let $content = $("<div>").addClass("content");
let $thumb = $("<div>").addClass("thumb");
let $img = $("<img>").attr("src", goodslist[i].goodsImages[0].path);
$thumb.append($img);
let $title = $("<div>").addClass("title").text(goodslist[i].name);
let $desc = $("<div>").addClass("desc").text(goodslist[i].remark.substring(10));
let $price = $("<div>").addClass("price");
let $span = $("<span>").text(goodslist[i].price + "元");
let $del = $("<del>").text(goodslist[i].price + "元");
$price.append($span).append($del);
$content.append($thumb).append($title).append($desc).append($price);
$a.append($content);
$boxitem.append($a);
container.append($boxitem);
}
}
<script>
基本的搜索功能,直接和数据库进行交互,通过sql语句完成商品数据的检索,但是企业项目中需要的搜索功能很少会直接和数据库进行交互,一般情况下会使用数据中间件框架elasticsearch
/**
* 根据名称模糊搜索商品数据
* @param name 商品名称
* @return 返回符合搜索条件的商品
*/
public List<Goods> searchGoodsWithName(String name) {
// 构建条件
GoodsExample ge = new GoodsExample();
ge.createCriteria().andNameLike("%" + name + "%");
return goodsMapper.selectByExample(ge);
}
控制层UtilsController
package com.damu.xiaomi.controller;
import com.damu.xiaomi.entry.Goods;
import com.damu.xiaomi.entry.GoodsType;
import com.damu.xiaomi.service.GoodsShippingService;
import com.damu.xiaomi.service.GoodsTypeService;
import com.damu.xiaomi.utils.ResponseMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/u")
public class UtilsController {
@Autowired
private GoodsShippingService goodsShippingService;
@Autowired
private GoodsTypeService goodsTypeService;
@GetMapping("/search/{name}")
@ResponseBody
public ResponseMessage searchGoodsWithName(@PathVariable String name) {
List<Goods> goodsList = goodsShippingService.searchGoodsWithName(name);
return goodsList.size() > 0 ? ResponseMessage.success().addObject("goodsList", goodsList): ResponseMessage.error();
}
}
单元测试
@Autowired
private GoodsShippingService goodsShippingService;
@Test
public void testSearchGoods() {
List<Goods> goodsList = goodsShippingService.searchGoodsWithName("小米");
goodsList.forEach(goods -> System.out.println(goods));
}
最后进行网页视图的开发就行了:/web/goodsList.jsp
<form action="#" id="search">
<input type="text">
<button>button>
form>
一级类型查看
首页导航–> 根据一级类型查看所有商品
二级类型查看
楼层商品–> 根据二级类型查看商品
业务层开发,添加方法
/**
* 根据商品类型查询商品
* @param goodsType 二级商品类型
* @return 属于指定商品类型的所有商品
*/
public List<Goods> findGoodsWithType(GoodsType goodsType) {
GoodsExample ge = new GoodsExample();
ge.createCriteria().andGoodsTypeIdEqualTo(goodsType.getId());
return goodsMapper.selectByExample(ge);
}
/**
* 根据一级类型查询商品数据
* @param goodstype 一级类型
* @return 返回所有商品
*/
public List<Goods> findGoodsWithToptype(GoodsType goodstype) {
// 根据一级类型查询所有所属二级类型
List<GoodsType> gt = goodsTypeService.findSecondLevel(goodstype);
// 查询所有二级类型下的所有商品
List<Goods> goodsList = new ArrayList<>();
for (GoodsType goodsType : gt) {
List<Goods> goods = this.findGoodsWithType(goodsType);
goodsList.addAll(goods);
}
return goodsList;
}
控制层添加方法
/**
* 根据类型查看商品
* @param level 类型级别 1 一级类型 2 二级类型
* @param goodsTypeId 类型编号
* @return 返回响应数据
*/
@GetMapping("/search/{level}/{goodsTypeId}")
@ResponseBody
public ResponseMessage searchGoodsWithType(@PathVariable String level,
@PathVariable String goodsTypeId) {
// 根据编号查询类型
GoodsType goodsType = goodsTypeService.findById(Integer.parseInt(goodsTypeId));
List<Goods> goodsList = null;
// 判断类型级别
if("1".equals(level)) {
goodsList = goodsShippingService.findGoodsWithToptype(goodsType);
} else if("2".equals(level)) {
goodsList = goodsShippingService.findGoodsWithType(goodsType);
}
return goodsList != null && goodsList.size() > 0
? ResponseMessage.success().addObject("goodsList", goodsList)
: ResponseMessage.error();
}
}
视图处理:/web/goodslist2.jsp
<script src="js/jquery/jquery-3.4.1.js">script>
<script>
$(function() {
// ?level=1&goodsTypeid=102
// 1. 获取搜索关键词
let $search = window.location.search.substring(1).split("&"); // [level=1, goodsTypeid=102]
let $level = $search[0].split("=")[1]; // 1
let $goodsTypeId = $search[1].split("=")[1]; // 102
console.log($level, $goodsTypeId);
// 2. Ajax请求,搜索商品
$.ajax({
url: "/xiaomi/u/search/" + $level + "/" + parseInt($goodsTypeId),
method: "GET",
success: function(response) {
console.log(response);
// 展示搜索到的商品
let $goodslist = response.objectMap.goodsList;
loadGoods($goodslist, $("#search_goods_box"))
},
error: function() {
console.log("搜索请求迷路了...");
}
});
function loadGoods(goodslist, container) {
// 遍历商品列表,将商品添加到页面汇总
for(let i = 0; i < goodslist.length; i++) {
let $boxitem = $("").addClass("goodsboxitem");
let $a = $("").attr("href", "/xiaomi/detail.jsp?gid=" + goodslist[i].id);
let $content = $("").addClass("content");
let $thumb = $("").addClass("thumb");
let $img = $("").attr("src", goodslist[i].goodsImages[0].path);
$thumb.append($img);
let $thumbx = $("").addClass("thumbnailx");
let $img2 = $("").attr("src", goodslist[i].goodsImages[1].path);
$thumbx.append($img2);
let $title = $("").addClass("title").text(goodslist[i].name);
let $desc = $("").addClass("desc").text(goodslist[i].remark.substring(10));
let $price = $("").addClass("price");
let $span = $("").text(goodslist[i].price + "元");
let $del = $("").text(goodslist[i].price + "元");
$price.append($span).append($del);
$content.append($thumb).append($title).append($desc).append($price).append($thumbx);
$a.append($content);
$boxitem.append($a);
container.append($boxitem);
}
}
});
script>
10. 商品详情
在GoodsShippingService中添加方法findGoodsWithId(…)
/**
* 根据id查询商品
* @param id 商品编号
* @return 返回查询到的商品
*/
public Goods findGoodsWithId(Integer id) {
return goodsMapper.selectByPrimaryKey(id);
}
商品详情控制器接口开发:GoodsController
package com.damu.xiaomi.controller;
import com.damu.xiaomi.entry.Goods;
import com.damu.xiaomi.service.GoodsShippingService;
import com.damu.xiaomi.utils.ResponseMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsShippingService goodsShippingService;
@GetMapping("/detail/{gid}")
@ResponseBody
public ResponseMessage findGoodsWithId(@PathVariable Integer gid) {
Goods goods = goodsShippingService.findGoodsWithId(gid);
return ResponseMessage.success().addObject("goods", goods);
}
}
最后进行网页视图处理即可,发送Ajax请求获取对应的商品信息
11. 购物车
商品加入购物车 detail.jsp 商品详情页 --> 加入购物车 --> 加入购物车流程
业务处理层:ShopCartService
package com.damu.xiaomi.service;
import com.damu.xiaomi.dao.GoodsCartMapper;
import com.damu.xiaomi.entry.Consumer;
import com.damu.xiaomi.entry.Goods;
import com.damu.xiaomi.entry.GoodsCart;
import com.damu.xiaomi.entry.GoodsCartExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Service
public class ShopCartService {
@Autowired
private GoodsCartMapper goodsCartMapper;
/**
* 指定商品加入购物车
* @param goods 要加入购物车的商品
* @return 返回加入结果
*/
public boolean addGoodsToShopCart(Consumer consumer, Goods goods) {
// 查询指定的商品在当前用户的购物车中是否存在
List<GoodsCart> cartList = this.chkGoods(consumer, goods);
if(cartList.size() > 0) {
// 更新购买数量
int count = cartList.get(0).getBuyCount();
cartList.get(0).setBuyCount(count+1);
// 更新小金额
double price = cartList.get(0).getSubtotal()/count * cartList.get(0).getBuyCount();
cartList.get(0).setSubtotal(price);
} else {
// 新增商品
GoodsCart gc = new GoodsCart(goods.getId(), 1, new Date(), goods.getPrice(), consumer.getId());
goodsCartMapper.insertSelective(gc);
}
return true;
}
/**
* 从购物车中删除指定的商品
* @param goods 要删除的商品
* @return 返回删除结果
*/
public boolean removeGoodsFromShopCart(Consumer consumer, Goods goods) {
// 判断商品在购物车中是否存在
List<GoodsCart> cartList = this.chkGoods(consumer, goods);
if(cartList.size() > 0) {
// 商品存在,删除
goodsCartMapper.deleteByPrimaryKey(cartList.get(0).getId());
return true;
}
System.out.println("商品不存在.");
return false;
}
/**
* 查询指定用户购物车中所有商品
* @param consumer 指定用户
* @return 返回该用户的所有商品
*/
public List<GoodsCart> findAllGoodsCartWithConsumer(Consumer consumer) {
GoodsCartExample gce = new GoodsCartExample();
gce.createCriteria().andConsumerIdEqualTo(consumer.getId());
return goodsCartMapper.selectByExample(gce);
}
/**
* 判断某个商品在购物车中是否存在
* @param consumer 所属用户
* @param goods 商品对象
* @return 商品数据
*/
private List<GoodsCart> chkGoods(Consumer consumer, Goods goods) {
// 查询指定的商品在当前用户的购物车中是否存在
GoodsCartExample gce = new GoodsCartExample();
gce.createCriteria().andConsumerIdEqualTo(consumer.getId())
.andGoodsIdEqualTo(goods.getId());
// 查询操作
List<GoodsCart> cartList = goodsCartMapper.selectByExample(gce);
return cartList;
}
}
重构登录方法
@Autowired
private ConsumerService consumerService;
@PostMapping(value="/login/auth", produces = {
"application/json;charset=UTF-8"})
@ResponseBody // 序列化--> 类型转换--> jackson --> json
public ResponseMessage login(@RequestParam String username,
@RequestParam String password,
HttpSession session) {
/*
重构:登录业务
1. 验证账号+密码
2. 登记用户 -- 会话跟踪【session】
*/
System.out.println("接受到请求:/consumer/login/auth");
System.out.println("账号:" + username + "; 密码:" + password);
Consumer consumer = new Consumer(username, password);
consumer = consumerService.findConsumerWithUsernameAndPassword(consumer);
System.out.println("登录结果:" + consumer);
// 记录登录用户
session.setAttribute("loginConsumer", consumer);
return consumer != null ? ResponseMessage.success() : ResponseMessage.error();
}
控制层ShopCartController
package com.damu.xiaomi.controller;
import com.damu.xiaomi.entry.Consumer;
import com.damu.xiaomi.entry.Goods;
import com.damu.xiaomi.entry.GoodsCart;
import com.damu.xiaomi.service.GoodsShippingService;
import com.damu.xiaomi.service.ShopCartService;
import com.damu.xiaomi.utils.ResponseMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
@RequestMapping("/shopcart")
public class ShopCartController {
@Autowired
private ShopCartService shopCartService;
@Autowired
private GoodsShippingService goodsShippingService;
@GetMapping("/add/{goodsId}")
@ResponseBody
public ResponseMessage addGoodsToCart(@PathVariable Integer goodsId, HttpSession session) {
// 获取当前登录用户
Consumer consumer = (Consumer) session.getAttribute("loginConsumer");
if (consumer == null) {
return ResponseMessage.error();
}
// 加入商品到购物车
Goods goods = goodsShippingService.findGoodsWithId(goodsId);
shopCartService.addGoodsToShopCart(consumer, goods);
return ResponseMessage.success();
}
@GetMapping("/remove/{goodsId}")
@ResponseBody
public ResponseMessage removeGoodsFromCart(@PathVariable Integer goodsId, HttpSession session) {
// 获取当前登录用户
Consumer consumer = (Consumer) session.getAttribute("loginConsumer");
if (consumer == null) {
return ResponseMessage.error();
}
Goods goods = goodsShippingService.findGoodsWithId(goodsId);
shopCartService.removeGoodsFromShopCart(consumer, goods);
return ResponseMessage.success();
}
@GetMapping("/chk")
@ResponseBody
public ResponseMessage findAllWithConsumer(HttpSession session) {
// 获取当前登录用户
Consumer consumer = (Consumer) session.getAttribute("loginConsumer");
if (consumer == null) {
return ResponseMessage.error();
}
List<GoodsCart> cartList = shopCartService.findAllGoodsCartWithConsumer(consumer);
return ResponseMessage.success().addObject("cartList", cartList);
}
}
网页视图处理
- 添加商品到购物车操作:在详情页面完成
- 查看购物车商品:在购物车页面完成