这是【SpringBoot企业微信点餐系统实战】系列第二篇
源码地址:https://github.com/cachecats/sell
一、依赖引入和数据库配置
编辑 pom.xml
引入 mysql
、jpa
、 lombok
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.solo</groupId>
<artifactId>sell</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sell</name>
<description>Demo project <span class="hljs-keyword">for</span> Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope><span class="hljs-built_in">test</span></scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
将 application.properties
改名为 application.yml
,不改也行但 yml
文件写起来更爽。配置数据库连接和 jpa
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
url: jdbc:mysql://127.0.0.1/sell?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
二、项目日志配置
最终选择了 LogBack
作为日志工具,配置如下:
在 项目目录/src/main/resources
目录下新建 logback 配置文件 logback-spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name=<span class="hljs-string">"consoleLog"</span> class=<span class="hljs-string">"ch.qos.logback.core.ConsoleAppender"</span>>
<layout class=<span class="hljs-string">"ch.qos.logback.classic.PatternLayout"</span>>
<pattern>
%d - %msg%n
</pattern>
</layout>
</appender>
<appender name=<span class="hljs-string">"fileInfoLog"</span> class=<span class="hljs-string">"ch.qos.logback.core.rolling.RollingFileAppender"</span>>
<filter class=<span class="hljs-string">"ch.qos.logback.classic.filter.LevelFilter"</span>>
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<encoder>
<pattern>
%msg%n
</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class=<span class="hljs-string">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span>>
<!--路径-->
<fileNamePattern>/Users/solo/Documents/project/springboot/<span class="hljs-built_in">log</span>/info.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<appender name=<span class="hljs-string">"fileErrorLog"</span> class=<span class="hljs-string">"ch.qos.logback.core.rolling.RollingFileAppender"</span>>
<filter class=<span class="hljs-string">"ch.qos.logback.classic.filter.ThresholdFilter"</span>>
<level>ERROR</level>
</filter>
<encoder>
<pattern>
%msg%n
</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class=<span class="hljs-string">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span>>
<!--路径-->
<fileNamePattern>/Users/solo/Documents/project/springboot/<span class="hljs-built_in">log</span>/error.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<root level=<span class="hljs-string">"info"</span>>
<appender-ref ref=<span class="hljs-string">"consoleLog"</span> />
<appender-ref ref=<span class="hljs-string">"fileInfoLog"</span> />
<appender-ref ref=<span class="hljs-string">"fileErrorLog"</span> />
</root>
</configuration>
这里配置了日志格式、每天生成日志文件到指定目录、error 和其他级别日志分开、滚动策略等,就不一一介绍了,把这个文件粘到项目中就可以。
三、商品类目dao、service层开发
上篇文章介绍了数据库的设计,今天就来开发具体业务吧。 开发顺序基本是每个表先写 dto类,再写 dao 层,再写 service 层,每步开发完都进行单元测试。
3.1 类目 dto
这个没什么难度,照着数据库表每个字段写下来就好,采用驼峰式命名规则。 为方便比较,先给出上篇的商品类目 product_category
sql创建语句
create table `product_category`(
`category_id` int not null auto_increment,
`category_name` varchar(64) not null comment '类目名字',
`category_type` int not null comment '类目编号',
`create_time` timestamp not null default current_timestamp comment '创建时间',
`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改时间',
primary key (`category_id`),
unique key `uqe_category_type` (`category_type`)
) comment '类目表';
新建 dto
包,在该包下新建 java
类 ProductCategory
package com.solo.sell.dto;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
商品分类
*/
@Entity
@DynamicUpdate
@Data
public class ProductCategory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer categoryId;
/** 种类名称 */
private String categoryName;
/** 种类类型 */
private Integer categoryType;
public <span class="hljs-function"><span class="hljs-title">ProductCategory</span></span>() {
}
public ProductCategory(String categoryName, Integer categoryType) {
this.categoryName = categoryName;
this.categoryType = categoryType;
}
}
说一下类名上面的三个注解: @Entity
:表示这是一个entity实体类 @DynamicUpdate
:因为数据库中updateTime
字段设置了自动更新,如果不加这个注解,自动更新将不会生效 @Data
:lombok 的辅助方法,可以自动生成 Get、Set、toString方法,官方文档介绍:
@Data
All together now: A shortcut for@ToString
,@EqualsAndHashCode
,@Getter
on all fields, and@Setter
on all non-final fields, and@RequiredArgsConstructor
!
注意:如果重写了构造方法,一定要加一个无参构造,否则后面会报错。
3.2 商品类目 repository
新建 repository
包,用来存放数据库操作的仓库。 数据库操作用 JPA
,新建接口ProductCategoryRepository
继承自 JpaRepository
.
public interface ProductCategoryRepository extends JpaRepository<ProductCategory, Integer>{
/**
* 传入类型列表,查询包含列表中类型的所有数据
*/
List<ProductCategory> findByCategoryTypeIn(List<Integer> types);
}
JpaRepository<ProductCategory, Integer>
里面有两个参数,第一个是数据表对应的实体类名,第二个是主键类型。 这里添加一个方法 findByCategoryTypeIn(List<Integer> types)
,传入商品类目的类型列表,返回包含这些类目的所有数据。
3.3 商品类目 service
新建 service
包,创建接口 ProductCategoryService
package com.solo.sell.service;
import com.solo.sell.dto.ProductCategory;
import java.util.List;
public interface ProductCategoryService {
ProductCategory findOne(Integer id);
List<ProductCategory> findAll();
ProductCategory save(ProductCategory productCategory);
List<ProductCategory> findByCategoryTypeIn(List<Integer> types);
}
在 service
包下新建 impl
存放 service
的实现类,并创建 ProductCategoryService
的实现类 ProductCategoryServiceImpl
package com.solo.sell.service.impl;
import com.solo.sell.dto.ProductCategory;
import com.solo.sell.repository.ProductCategoryRepository;
import com.solo.sell.service.ProductCategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 类目服务实现
*/
@Service
public class ProductCategoryServiceImpl implements ProductCategoryService {
@Autowired
ProductCategoryRepository repository;
@Override
public ProductCategory findOne(Integer id) {
return repository.findById(id).get();
}
@Override
public List<ProductCategory> findAll() {
return repository.findAll();
}
@Override
public ProductCategory save(ProductCategory productCategory) {
return repository.save(productCategory);
}
@Override
public List<ProductCategory> findByCategoryTypeIn(List<Integer> types) {
return repository.findByCategoryTypeIn(types);
}
}
四、单元测试
对 ProductCategoryRepository
和 ProductCategoryServiceImpl
都编写单元测试。
ProductCategoryRepository
的单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductCategoryRepositoryTest {
<span class="hljs-meta">@Autowired</span>
<span class="hljs-keyword">private</span> ProductCategoryRepository repository;
<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Transactional</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">()</span> </span>{
ProductCategory category = <span class="hljs-keyword">new</span> ProductCategory(<span class="hljs-string">"女生最爱"</span>, <span class="hljs-number">1</span>);
ProductCategory save = repository.save(category);
Assert.assertNotNull(save);
}
<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">findOne</span><span class="hljs-params">()</span> </span>{
ProductCategory productCategory = repository.findById(<span class="hljs-number">1</span>).get();
Assert.assertNotNull(productCategory);
}
<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@Transactional</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">()</span> </span>{
ProductCategory category = repository.findById(<span class="hljs-number">1</span>).get();
category.setCategoryType(<span class="hljs-number">4</span>);
ProductCategory save = repository.save(category);
Assert.assertNotNull(save);
}
<span class="hljs-comment">/**
* 传入类型列表,查询包含列表中类型的所有数据
*/</span>
<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">findByCategoryType</span><span class="hljs-params">()</span> </span>{
List<Integer> types = Arrays.asList(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>);
List<ProductCategory> list = repository.findByCategoryTypeIn(types);
Assert.assertNotEquals(<span class="hljs-number">0</span>, list.size());
}
}
ProductCategoryServiceImpl
的单元测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductCategoryServiceImplTest {
@Autowired
ProductCategoryServiceImpl service;
@Test
public void findOne() {
ProductCategory one = service.findOne(1);
Assert.assertEquals(new Integer(1), one.getCategoryId());
}
@Test
public void findAll() {
List<ProductCategory> list = service.findAll();
Assert.assertNotEquals(0, list.size());
}
@Test
public void save() {
ProductCategory cate = service.save(new ProductCategory("热销榜", 5));
Assert.assertNotNull(cate);
}
@Test
public void findByCategoryTypeIn() {
List<ProductCategory> list = service.findByCategoryTypeIn(Arrays.asList(3, 4, 5, 6));
Assert.assertNotEquals(0, list.size());
}
}
注意:在测试方法上加注解 @Transactional ,会在测试之后把测试中操作的数据库全部回滚,不会因为测试污染数据库。
今天就到这,下次见~
源码地址:https://github.com/cachecats/sell