2、mybatis逆向工程
逆向工程是通过数据库中已经存在的数据表,反向生成Java中的实体类Java
中的实体类(生成对应的持久层代码)
1、创建商城项目
代码生成器作为商城项目的一个工具组件存在。
maven
项目
引入逆向工程依赖
- mybatis-generator-core
- mybatis-connector-java
mysql
mysql-connector-java
8.0.19
org.mybatis.generator
mybatis-generator-core
1.4.0
2、配置生成规则
逆向工程的生成规则,就是描述数据库中的那些表,生成对应的Java中的实体类,同时生成映射配置文件。这个生成规则就是一个普通的配置文件。
在项目的主目录中创建一个配置文件:generator.xml
3、逆向工程
通过配置文件指定的生成规则,自动构建实体类和数据访问类,参考官方文档
public class CodeGenerator {
public static void main(String[] args) throws Exception{
List warnings = new ArrayList();
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);
}
}
3、SSM项目整合
(1)配置文件整合
spring配置文件:src/main/resources/applicationContext.xml
springMVC配置文件:src/main/resources/springMVC.xml
mybatis配置文件:src/main/rescources/mybatis-config.xml
数据源配置文件:scr/main/resoucres/db.properties
在启动时,框架初始化需要在web.xml
中添加启动配置
在项目中创建对应的配置文件,同时添加web支持
(2)依赖添加
- spring-core
- spring-context
- spring-beans
- spring-expression
- spring-insuiession
- spring-jdbc
- spring-orm
- spring-web
- spring-webmvc
- mybatis
- mybatis-spring
- c3p0
- mysql-connector-java
pom.xml
4.0.0
org.example
Xiaomi
1.0-SNAPSHOT
mysql
mysql-connector-java
8.0.19
org.mybatis.generator
mybatis-generator-core
1.4.0
org.springframework
spring-core
5.2.1.RELEASE
org.springframework
spring-beans
5.2.1.RELEASE
org.springframework
spring-context
5.2.1.RELEASE
org.springframework
spring-expression
5.2.1.RELEASE
org.springframework
spring-jdbc
5.2.1.RELEASE
org.springframework
spring-orm
5.2.1.RELEASE
org.springframework
spring-web
5.2.1.RELEASE
org.springframework
spring-webmvc
5.2.1.RELEASE
org.mybatis
mybatis
3.5.3
org.mybatis
mybatis-spring
2.0.3
com.mchange
c3p0
0.9.5.4
(3)细化配置信息
4、单元测试
(1)引入依赖
基于spring
的单元测试,需要以下依赖
spring-test
junit
spring-aop
aspectj
junit
junit
4.12
test
org.springframework
spring-test
5.2.1.RELEASE
org.springframework
spring-aop
5.2.1.RELEASE
org.aspectj
aspectjweaver
1.9.5
(2)单元测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class DaoTest {
@Autowired
private ConsumerMapper consumerMapper;
@Test
public void testConsumerInsert(){
Consumer consumer = new Consumer("zfz","123");
//讲consumer添加到数据库 insertSelective在只有部分属性有值时使用
consumerMapper.insertSelective(consumer);
System.out.println("insertSelective执行完成");
}
}
5、业务模型开发&封装响应
从业务模型处理>>复杂业务模型操作
登录、注册>>首页数据加载-->搜索>>购物车>>订单
(1)登录业务
首先创建用户相关业务处理类:com.zfz.xiaomi.service.ConsumerService.java
@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 consumerList = consumerMapper.selectByExample(ce);
return consumerList != null && consumerList.size() == 1;
}
}
创建用户相关业务的访问接口/控制器:com.zfz.xiaomi.controller.ConsumerController.java
@Controller
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private ConsumerService consumerService;
@PostMapping("/login/auth")
public String 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业务的单元测试
@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 {
//发送post请求
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/consumer/login/auth")
.param("username", "zfz")
.param("password", "123")).andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
}
(2)相应数据封装
关于数据接口,提供给客户端调用,并且返回符合预期标准的数据访问接口,通常会有如下的要求
- 固定格式的参数,根据需求提供调用接口即可
- 返回数据-错误码:快捷判断响应结果是否正确的错误标志,HTTP:200 正确;404 未找到资源; 500 服务器内部错误
- 返回数据-错误描述:针对错误码具体错误信息的描述
- 返回数据-数据封装:具体包含的一个或者多个数据
在项目中定义工具类型,封装响应数据:com.zfz.xiaomi.utils.ResponsMessage.java
public class ResponseMessage {
private String errorCode;
private String errMsg;
private Map objectMap = new HashMap<>();
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public Map getObjectMap() {
return objectMap;
}
public void setObjectMap(Map 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.setErrMsg("处理成功");
return rm;
}
public static ResponseMessage error(){
ResponseMessage rm = new ResponseMessage();
rm.setErrorCode("200");
rm.setErrMsg("处理失败");
return rm;
}
}
重构登陆业务
@PostMapping("/login/auth")
@ResponseBody //@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
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 ? ResponseMessage.success() : ResponseMessage.error();
}
6、登录
7、用户注册
业务层:com.zfz.xiaomi.service.ConsumerService.java
添加注册方法:register()
public String register(Consumer consumer){
//验证用户名是否存在
ConsumerExample ce = new ConsumerExample();
ce.createCriteria().andUsernameEqualTo(consumer.getUsername());
List consumerList = consumerMapper.selectByExample(ce);
if(consumerList.size() > 0){
return "注册失败,用户名已存在";
}
return "注册成功";
}
业务层:com.zfz.xiaomi.service.ConsumerController.java
添加注册方法:register()
@PostMapping(value = "/register",produces = {"application/json;charset=UTF-8"})
@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);
}
网页视图及功能完善
web/register.jsp
+'jquery'脚本
8、首页数据加载
首页中需要的数据从后台获取并添加
- 商品类型
- 每种类型下的商品
开发业务处理层
商品类型:
com.zfz.xiaomi.service.GoodsTypeService.java
-
商品:
com.zfz.xiaomi.service.GoodsShippingService.java
反例:项目设计时充分考虑不同的代码层、组件模块之间的命名规则和规范。避免出现不同模块、不同代码层出现相同/相似名称的类型,降低代码质量和可读性
GoodsTypeService.java
@Service
public class GoodsTypeService {
@Autowired
private GoodsTypeMapper goodsTypeMapper;
/**
* 查询一级商品类型
* @return 返回所有的一级商品类型
*/
public List findTopLevel(){
GoodsTypeExample gte = new GoodsTypeExample();
gte.createCriteria().andPidIsNotNull();
return goodsTypeMapper.selectByExample(gte);
}
/**
* 查询二级商品类型
* @param top 一级商品类型
* @return 返回对应的二级商品类型
*/
public List findSecondLevel(GoodsType top){
GoodsTypeExample gte = new GoodsTypeExample();
gte.createCriteria().andPidEqualTo(top.getId());
return goodsTypeMapper.selectByExample(gte);
}
}
GoodsShippingService.java
@Service
public class GoodsShippingService {
@Autowired
private GoodsMapper goodsMapper;
/**
* 根据商品类型查询商品
* @param goodsType 商品类型
* @return 属于指定商品类型的所有商品
*/
public List findGoodsWithType(GoodsType goodsType){
GoodsExample ge = new GoodsExample();
ge.createCriteria().andGoodsTypeIdEqualTo(goodsType.getId());
return goodsMapper.selectByExample(ge);
}
}
首页数据加载完善
生成的Goods类中不包含图片的地址,在类中添加图片地址的属性
com.zfz.xiaomi.entry.Goods.java
private List goodsImages;
public List getGoodsImages() {
return goodsImages;
}
public void setGoodsImages(List goodsImages) {
this.goodsImages = goodsImages;
}
mapper/GoodsMapper.xml
mapper/GoodsMapper.xml
开发首页控制器com.zfz.xiaomi.controller.IndexController.java
- initIndex(..):加载首页数据的控制器方法,前端网页中通过JQuery Ajax请求获取数据
开发首页视图:/web/index.jsp
渲染展示商品
9、商品搜索
基本搜索功能,直接和数据库交互,通过sql语句完成商品数据的检索
企业项目需要的搜索功能,很少会直接和数据库进行交互,一般情况下会使用数据中间件框架elasticesearch
(1)、商品按名称模糊搜索
业务层方法开发:com.zfz.xiaomi.service.GoodsShipService.java
/**
* 根据名称模糊搜索商品数据
* @param name 商品名称
* @return 返回符合条件的商品
*/
public List searchGoodsWithName(String name ){
GoodsExample ge = new GoodsExample();
ge.createCriteria().andNameLike("%" + name + "%");
return goodsMapper.selectByExample(ge);
}
控制层方法com.zfz.xiaomi.controller.UtilsController.java
@GetMapping("/search/{name}")
@ResponseBody
private ResponseMessage searchGoodsWithName(@PathVariable String name){
List goodsList = goodsShippingService.searchGoodsWithName(name);
return goodsList.size() > 0 ? ResponseMessage.success().addObject("goodsList", goodsList)
: ResponseMessage.error();
}
单元测试
@Test
public void testSearchGoods(){
List goodsList = goodsShippingService.searchGoodsWithName("小米");
goodsList.forEach(goods -> System.out.println(goods));
}
网页视图开发
(2)、商品按类型检索
一级类型查看
首页导>>根据一级类型查看所有商品
业务层:com.zfz.xiaomi.service.GoodsShippingService
/**
* 根据二级类型查询商品数据
* @param goodsType 一级类型
* @return 返回所有商品
*/
public List findGoodsWithTopType(GoodsType goodsType){
//查询一级类型下的所有二级类型
List gt = goodsTypeService.findSecondLevel(goodsType);
//查询所有二级类型下所有商品
List goodsList = new ArrayList<>();
for (GoodsType goodsType1 : gt){
List goodses = this.findGoodsWithType(goodsType1);
goodsList.addAll(goodses);
}
return goodsList;
}
控制层:com.zfz.xiaomi.controller.UtilsController
/**
* 根据类型查看商品
* @param level level 类型级别 1一级类型 2 二级类型
* @param goodTypeId 类型编号
* @return 返回响应数据
*/
@GetMapping("search/{level}/{gtId}")
public ResponseMessage searchGoodsWithType(@PathVariable Integer level, @PathVariable Integer goodTypeId){
GoodsType goodsType = goodsTypeService.findById(goodTypeId);
List goodsList = null;
if (level == 1){
goodsList = goodsShippingService.findGoodsWithTopType(goodsType);
} else if (level == 2){
goodsList = goodsShippingService.findGoodsWithType(goodsType);
}
return goodsList != null && goodsList.size() > 0
? ResponseMessage.success().addObject("goodsList", goodsList)
: ResponseMessage.error();
}
视图处理web/goodslist2.jsp
-
index.jsp
导航发起 - 跳转到指定页面
goodslist2.jsp
- 在商品列表页面中,获取查询参数,发送请求获取数据
二级类型查看
楼层商品>>根据二级类型查看更多
10、商品详情处理
业务模型处理方法:com.zfz.xiaomi.service.GoodsShippingService.java
开发业务层处理方法:findGoodsWithId(..)
,完善对应的GoodsMapper.xml
/**
* 根据id查询商品
* @param id 商品的id
* @return 查询到的商品
*/
public Goods findGoodWithId(Integer id){
return goodsMapper.selectByPrimaryKey(id);
}
商品详情控制器接口开发:com.zfz.xiaomi.controller.GoodsController.java
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsShippingService goodsShippingService;
@GetMapping("/detail/{gid}")
@ResponseBody
public ResponseMessage findGoodsWithId(@PathVariable Integer gid){
Goods goods = goodsShippingService.findGoodWithId(gid);
return ResponseMessage.success().addObject("goods",goods);
}
}
网页试图处理:‘web/detail.jsp’
跳转index.jsp||goodslist.jsp>>detail.jsp?gid=***
发送Ajax请求获取对应的商品信息
11、购物车
商品加入购物车
detail.jsp
商品详情页>>加入购物车>>加入购物车流程
业务处理层:com.damu.xiaomi.service.ShopCartService.java
public class ShopCartService {
@Autowired
private GoodsCartMapper goodsCartMapper;
/**
* 判断某个商品在购物车中是否存在
* @param consumer 指定用户
* @param goods 指定商品
* @return
*/
private List checkGoods(Consumer consumer, Goods goods){
//查询指定的商品在当前用户的购物车中是否存在
GoodsCartExample gce = new GoodsCartExample();
gce.createCriteria().andConsumerIdEqualTo(consumer.getId()).andGoodsIdEqualTo(goods.getId());
//查询操作
List cartList = goodsCartMapper.selectByExample(gce);
return cartList;
}
/**
* 指定商品加入购物车
* @param goods 要加入购物车的商品
* @return 返回加入结果
*/
public boolean addGoodsToShopCart(Consumer consumer,Goods goods){
List cartList = this.checkGoods(consumer, goods);
if (cartList.size() > 0){
//更新购买数量
int count = cartList.get(0).getBuyCount();
cartList.get(0).setBuyCount(count + 1);
//更新小计金额
double totalPrice = cartList.get(0).getSubtotal()/count * cartList.get(0).getBuyCount();
cartList.get(0).setSubtotal(totalPrice);
}else {
//新增商品
GoodsCart goodsCart = new GoodsCart(goods.getId(),1,new Date(), goods.getPrice(), consumer.getId());
goodsCartMapper.insertSelective(goodsCart);
}
return true;
}
/**
* 从购物车中删除商品
* @param goods 要删除的商品
* @return 返回删除结果
*/
public boolean removeGoodsFromShopCart(Consumer consumer,Goods goods){
List cartList = this.checkGoods(consumer, goods);
if (cartList.size() > 0){
goodsCartMapper.deleteByPrimaryKey(cartList.get(0).getId());
return true;
}
System.out.println("商品不存在");
return false;
}
/**
* 查询指定用户购物车中的所有商品
* @param consumer 指定用户
* @return 返回该用户购物车的所有商品
*/
public List findAllGoodsCartWithConsumer(Consumer consumer){
GoodsCartExample gce = new GoodsCartExample();
gce.createCriteria().andConsumerIdEqualTo(consumer.getId());
return goodsCartMapper.selectByExample(gce);
}
}
重构登陆业务com.zfz.xiaomi.controller.ConsumerCroller.java
- 验证密码+账号
- 登记用户 -- 会话跟踪【session】
@PostMapping(value = "/login/auth",produces = {"application/json;charset=UTF-8"})
@ResponseBody //@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
public ResponseMessage login(@RequestParam String username, @RequestParam String password,
HttpSession 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();
}
控制层:com.zfz.xiaomi.controller.ShopCartController.java
@Controller
@RequestMapping("/shopcart")
public class ShopCartController {
@Autowired
private ShopCartService shopCartService;
@Autowired
private GoodsShippingService goodsShippingService;
/**
* 商品加入购物车
* @param goodsId 商品id
* @param session session中携带consumer信息
* @return 成功或失败
*/
@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.findGoodWithId(goodsId);
shopCartService.addGoodsToShopCart(consumer,goods);
return ResponseMessage.success();
}
/**
* 从购物车中删除指定商品
* @param goodsId 商品id
* @param session session中携带consumer信息
* @return 成功或失败
*/
@GetMapping("/remove/{goodsId}")
public ResponseMessage removeGoodsToCart(@PathVariable Integer goodsId, HttpSession session){
//获取当前用户
Consumer consumer = (Consumer) session.getAttribute("loginConsumer");
if (consumer == null)
return ResponseMessage.error();
Goods goods = goodsShippingService.findGoodWithId(goodsId);
shopCartService.removeGoodsFromShopCart(consumer,goods);
return ResponseMessage.success();
}
@GetMapping("/chk")
public ResponseMessage findAllWithConsumer(HttpSession session){
//获取当前用户
Consumer consumer = (Consumer) session.getAttribute("loginConsumer");
if (consumer == null)
return ResponseMessage.error();
List cartList = shopCartService.findAllGoodsCartWithConsumer(consumer);
return ResponseMessage.success().addObject("cartList",cartList);
}
}
网页视图处理
- 添加商品到购物车的操作:详情页完成
- 查看购物车商品:购物车页面
- 从购物车中删除对应的商品:购物车页面