让咱们使用领域驱动的方式,构建一个简单的系统。html
新闻系统的需求以下:java
你们以为,针对上面需求,大概须要多长时间能够完成,能够先写下来。mysql
构建项目,使用 start.spring.io 或使用模板工程,构建咱们的项目(Sprin Boot 项目),在这就很少叙述。git
首先,添加 gh-ddd-lite 相关依赖和插件。web
<?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.geekhalo</groupId>
<artifactId>gh-ddd-lite-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>com.geekhalo</groupId>
<artifactId>gh-base-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<properties>
<service.name>demo</service.name>
<server.name>gh-${service.name}-service</server.name>
<server.version>v1</server.version>
<server.description>${service.name} Api</server.description>
<servlet.basePath>/${service.name}-api</servlet.basePath>
</properties>
<dependencies>
<dependency>
<groupId>com.geekhalo</groupId>
<artifactId>gh-ddd-lite</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.geekhalo</groupId>
<artifactId>gh-ddd-lite-spring</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.geekhalo</groupId>
<artifactId>gh-ddd-lite-codegen</artifactId>
<version>1.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>true</executable>
<layout>ZIP</layout>
</configuration>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
<!--<processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>-->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
复制代码
在 application.properties 文件中添加数据库相关配置。spring
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/db_test?useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=
spring.application.name=ddd-lite-demo
server.port=8090
management.endpoint.beans.enabled=true
management.endpoint.conditions.enabled=true
management.endpoints.enabled-by-default=false
management.endpoints.web.exposure.include=beans,conditions,env
复制代码
新建 UserApplication 做为应用入口类。sql
@SpringBootApplication
@EnableSwagger2
public class UserApplication {
public static void main(String... args){
SpringApplication.run(UserApplication.class, args);
}
}
复制代码
使用 SpringBootApplication 和 EnableSwagger2 启用 Spring Boot 和 Swagger 特性。数据库
首先,咱们对新闻类型进行建模。apache
新闻类别状态,用于描述启用、禁用两个状态。在这使用 enum 实现。api
/**
* GenCodeBasedEnumConverter 自动生成 CodeBasedNewsCategoryStatusConverter 类
*/
@GenCodeBasedEnumConverter
public enum NewsCategoryStatus implements CodeBasedEnum<NewsCategoryStatus> {
ENABLE(1),
DISABLE(0);
private final int code;
NewsCategoryStatus(int code) {
this.code = code;
}
@Override
public int getCode() {
return code;
}
}
复制代码
NewsCategory 用于描述新闻类别,其中包括状态、名称等。
/**
* EnableGenForAggregate 自动建立聚合相关的 Base 类
*/
@EnableGenForAggregate
@Data
@Entity
@Table(name = "tb_news_category")
public class NewsCategory extends JpaAggregate {
private String name;
@Setter(AccessLevel.PRIVATE)
@Convert(converter = CodeBasedNewsCategoryStatusConverter.class)
private NewsCategoryStatus status;
}
复制代码
在命令行或ida中执行maven命令,以对项目进行编译,从而触发代码的自动生成。
mvn clean compile
复制代码
咱们使用 NewsCategory 的静态工厂,完成其建立逻辑。
首先,须要建立 NewsCategoryCreator,做为工程参数。
public class NewsCategoryCreator extends BaseNewsCategoryCreator<NewsCategoryCreator>{
}
复制代码
其中 BaseNewsCategoryCreator 为框架自动生成的,具体以下:
@Data
public abstract class BaseNewsCategoryCreator<T extends BaseNewsCategoryCreator> {
@Setter(AccessLevel.PUBLIC)
@Getter(AccessLevel.PUBLIC)
@ApiModelProperty(
value = "",
name = "name"
)
private String name;
public void accept(NewsCategory target) {
target.setName(getName());
}
}
复制代码
接下来,须要建立静态工程,并完成 NewsCategory 的初始化。
/**
* 静态工程,完成 NewsCategory 的建立
* @param creator
* @return
*/
public static NewsCategory create(NewsCategoryCreator creator){
NewsCategory category = new NewsCategory();
creator.accept(category);
category.init();
return category;
}
/**
* 初始化,默认状态位 ENABLE
*/
private void init() {
setStatus(NewsCategoryStatus.ENABLE);
}
复制代码
更新逻辑,只对 name 进行更新操做。
首先,建立 NewsCategoryUpdater 做为,更新方法的参数。
public class NewsCategoryUpdater extends BaseNewsCategoryUpdater<NewsCategoryUpdater>{
}
复制代码
一样,BaseNewsCategoryUpdater 也是框架自动生成,具体以下:
@Data
public abstract class BaseNewsCategoryUpdater<T extends BaseNewsCategoryUpdater> {
@Setter(AccessLevel.PRIVATE)
@Getter(AccessLevel.PUBLIC)
@ApiModelProperty(
value = "",
name = "name"
)
private DataOptional<String> name;
public T name(String name) {
this.name = DataOptional.of(name);
return (T) this;
}
public T acceptName(Consumer<String> consumer) {
if(this.name != null){
consumer.accept(this.name.getValue());
}
return (T) this;
}
public void accept(NewsCategory target) {
this.acceptName(target::setName);
}
}
复制代码
添加 update 方法:
/**
* 更新
* @param updater
*/
public void update(NewsCategoryUpdater updater){
updater.accept(this);
}
复制代码
启用,主要是对 status 的操做.
代码以下:
/**
* 启用
*/
public void enable(){
setStatus(NewsCategoryStatus.ENABLE);
}
复制代码
禁用,主要是对 status 的操做。
代码以下:
/**
* 禁用
*/
public void disable(){
setStatus(NewsCategoryStatus.DISABLE);
}
复制代码
至此,NewsCategory 的 Command 就建模完成,让咱们整体看下 NewsCategory:
/**
* EnableGenForAggregate 自动建立聚合相关的 Base 类
*/
@EnableGenForAggregate
@Data
@Entity
@Table(name = "tb_news_category")
public class NewsCategory extends JpaAggregate {
private String name;
@Setter(AccessLevel.PRIVATE)
@Convert(converter = CodeBasedNewsCategoryStatusConverter.class)
private NewsCategoryStatus status;
private NewsCategory(){
}
/**
* 静态工程,完成 NewsCategory 的建立
* @param creator
* @return
*/
public static NewsCategory create(NewsCategoryCreator creator){
NewsCategory category = new NewsCategory();
creator.accept(category);
category.init();
return category;
}
/**
* 更新
* @param updater
*/
public void update(NewsCategoryUpdater updater){
updater.accept(this);
}
/**
* 启用
*/
public void enable(){
setStatus(NewsCategoryStatus.ENABLE);
}
/**
* 禁用
*/
public void disable(){
setStatus(NewsCategoryStatus.DISABLE);
}
/**
* 初始化,默认状态位 ENABLE
*/
private void init() {
setStatus(NewsCategoryStatus.ENABLE);
}
}
复制代码
查找逻辑主要由 NewsCategoryRepository 完成。
新建 NewsCategoryRepository,以下:
/**
* GenApplication 自动将该接口中的方法添加到 BaseNewsCategoryRepository 中
*/
@GenApplication
public interface NewsCategoryRepository extends BaseNewsCategoryRepository{
@Override
Optional<NewsCategory> getById(Long aLong);
}
复制代码
一样, BaseNewsCategoryRepository 也是自动生成的。
interface BaseNewsCategoryRepository extends SpringDataRepositoryAdapter<Long, NewsCategory>, Repository<NewsCategory, Long>, QuerydslPredicateExecutor<NewsCategory> {
}
复制代码
领域对象 NewsCategory 不该该暴露到其余层,所以,咱们使用 DTO 模式处理数据的返回,新建 NewsCategoryDto,具体以下:
public class NewsCategoryDto extends BaseNewsCategoryDto{
public NewsCategoryDto(NewsCategory source) {
super(source);
}
}
复制代码
BaseNewsCategoryDto 为框架自动生成,以下:
@Data
public abstract class BaseNewsCategoryDto extends JpaAggregateVo implements Serializable {
@Setter(AccessLevel.PACKAGE)
@Getter(AccessLevel.PUBLIC)
@ApiModelProperty(
value = "",
name = "name"
)
private String name;
@Setter(AccessLevel.PACKAGE)
@Getter(AccessLevel.PUBLIC)
@ApiModelProperty(
value = "",
name = "status"
)
private NewsCategoryStatus status;
protected BaseNewsCategoryDto(NewsCategory source) {
super(source);
this.setName(source.getName());
this.setStatus(source.getStatus());
}
}
复制代码
至此,领域的建模工做已经完成,让咱们对 Application 进行构建。
/**
* GenController 自动将该类中的方法,添加到 BaseNewsCategoryController 中
*/
@GenController("com.geekhalo.ddd.lite.demo.controller.BaseNewsCategoryController")
public interface NewsCategoryApplication extends BaseNewsCategoryApplication{
@Override
NewsCategory create(NewsCategoryCreator creator);
@Override
void update(Long id, NewsCategoryUpdater updater);
@Override
void enable(Long id);
@Override
void disable(Long id);
@Override
Optional<NewsCategoryDto> getById(Long aLong);
}
复制代码
自动生成的 BaseNewsCategoryApplication 以下:
public interface BaseNewsCategoryApplication {
Optional<NewsCategoryDto> getById(Long aLong);
NewsCategory create(NewsCategoryCreator creator);
void update(@Description("主键") Long id, NewsCategoryUpdater updater);
void enable(@Description("主键") Long id);
void disable(@Description("主键") Long id);
}
复制代码
得益于咱们的 EnableGenForAggregate 和 GenApplication 注解,BaseNewsCategoryApplication 包含咱们想要的 Command 和 Query 方法。
接口已经准备好了,接下来,处理实现类,具体以下:
@Service
public class NewsCategoryApplicationImpl extends BaseNewsCategoryApplicationSupport
implements NewsCategoryApplication {
@Override
protected NewsCategoryDto convertNewsCategory(NewsCategory src) {
return new NewsCategoryDto(src);
}
}
复制代码
自动生成的 BaseNewsCategoryApplicationSupport 以下:
abstract class BaseNewsCategoryApplicationSupport extends AbstractApplication implements BaseNewsCategoryApplication {
@Autowired
private DomainEventBus domainEventBus;
@Autowired
private NewsCategoryRepository newsCategoryRepository;
protected BaseNewsCategoryApplicationSupport(Logger logger) {
super(logger);
}
protected BaseNewsCategoryApplicationSupport() {
}
protected NewsCategoryRepository getNewsCategoryRepository() {
return this.newsCategoryRepository;
}
protected DomainEventBus getDomainEventBus() {
return this.domainEventBus;
}
protected <T> List<T> convertNewsCategoryList(List<NewsCategory> src,
Function<NewsCategory, T> converter) {
if (CollectionUtils.isEmpty(src)) return Collections.emptyList();
return src.stream().map(converter).collect(Collectors.toList());
}
protected <T> Page<T> convvertNewsCategoryPage(Page<NewsCategory> src,
Function<NewsCategory, T> converter) {
return src.map(converter);
}
protected abstract NewsCategoryDto convertNewsCategory(NewsCategory src);
protected List<NewsCategoryDto> convertNewsCategoryList(List<NewsCategory> src) {
return convertNewsCategoryList(src, this::convertNewsCategory);
}
protected Page<NewsCategoryDto> convvertNewsCategoryPage(Page<NewsCategory> src) {
return convvertNewsCategoryPage(src, this::convertNewsCategory);
}
@Transactional(
readOnly = true
)
public <T> Optional<T> getById(Long aLong, Function<NewsCategory, T> converter) {
Optional<NewsCategory> result = this.getNewsCategoryRepository().getById(aLong);
return result.map(converter);
}
@Transactional(
readOnly = true
)
public Optional<NewsCategoryDto> getById(Long aLong) {
Optional<NewsCategory> result = this.getNewsCategoryRepository().getById(aLong);
return result.map(this::convertNewsCategory);
}
@Transactional
public NewsCategory create(NewsCategoryCreator creator) {
NewsCategory result = creatorFor(this.getNewsCategoryRepository())
.publishBy(getDomainEventBus())
.instance(() -> NewsCategory.create(creator))
.call();
logger().info("success to create {} using parm {}",result.getId(), creator);
return result;
}
@Transactional
public void update(@Description("主键") Long id, NewsCategoryUpdater updater) {
NewsCategory result = updaterFor(this.getNewsCategoryRepository())
.publishBy(getDomainEventBus())
.id(id)
.update(agg -> agg.update(updater))
.call();
logger().info("success to update for {} using parm {}", id, updater);
}
@Transactional
public void enable(@Description("主键") Long id) {
NewsCategory result = updaterFor(this.getNewsCategoryRepository())
.publishBy(getDomainEventBus())
.id(id)
.update(agg -> agg.enable())
.call();
logger().info("success to enable for {} using parm ", id);
}
@Transactional
public void disable(@Description("主键") Long id) {
NewsCategory result = updaterFor(this.getNewsCategoryRepository())
.publishBy(getDomainEventBus())
.id(id)
.update(agg -> agg.disable())
.call();
logger().info("success to disable for {} using parm ", id);
}
}
复制代码
该类中包含咱们想要的全部实现。
NewsInfoApplication 构建完成后,新建 NewsCategoryController 将其暴露出去。
新建 NewsCategoryController, 以下:
@RequestMapping("news_category")
@RestController
public class NewsCategoryController extends BaseNewsCategoryController{
}
复制代码
是的,核心逻辑都在自动生成的 BaseNewsCategoryController 中:
abstract class BaseNewsCategoryController {
@Autowired
private NewsCategoryApplication application;
protected NewsCategoryApplication getApplication() {
return this.application;
}
@ResponseBody
@ApiOperation(
value = "",
nickname = "create"
)
@RequestMapping(
value = "/_create",
method = RequestMethod.POST
)
public ResultVo<NewsCategory> create(@RequestBody NewsCategoryCreator creator) {
return ResultVo.success(this.getApplication().create(creator));
}
@ResponseBody
@ApiOperation(
value = "",
nickname = "update"
)
@RequestMapping(
value = "{id}/_update",
method = RequestMethod.POST
)
public ResultVo<Void> update(@PathVariable("id") Long id,
@RequestBody NewsCategoryUpdater updater) {
this.getApplication().update(id, updater);
return ResultVo.success(null);
}
@ResponseBody
@ApiOperation(
value = "",
nickname = "enable"
)
@RequestMapping(
value = "{id}/_enable",
method = RequestMethod.POST
)
public ResultVo<Void> enable(@PathVariable("id") Long id) {
this.getApplication().enable(id);
return ResultVo.success(null);
}
@ResponseBody
@ApiOperation(
value = "",
nickname = "disable"
)
@RequestMapping(
value = "{id}/_disable",
method = RequestMethod.POST
)
public ResultVo<Void> disable(@PathVariable("id") Long id) {
this.getApplication().disable(id);
return ResultVo.success(null);
}
@ResponseBody
@ApiOperation(
value = "",
nickname = "getById"
)
@RequestMapping(
value = "/{id}",
method = RequestMethod.GET
)
public ResultVo<NewsCategoryDto> getById(@PathVariable Long id) {
return ResultVo.success(this.getApplication().getById(id).orElse(null));
}
}
复制代码
至此,咱们的代码就彻底准备好了,如今须要准备建表语句。
使用 Flyway 做为数据库的版本管理,在 resources/db/migration 新建 V1.002__create_news_category.sql 文件,具体以下:
create table tb_news_category
(
id bigint auto_increment primary key,
name varchar(32) null,
status tinyint null,
create_time bigint not null,
update_time bigint not null,
version tinyint not null
);
复制代码
至此,咱们就完成了 NewsCategory 的开发。 执行 maven 命令,启动项目:
mvn clean spring-boot:run
复制代码
浏览器中输入 http://127.0.0.1:8090/swagger-ui.html , 经过 swagger 查看咱们的成果。
能够看到以下
在 NewsCategory 的建模过程当中,咱们的主要精力放在了 NewsCategory 对象上,其余部分基本都是框架帮咱们生成的。既然框架为咱们作了那么多工做,为何还须要咱们新建 NewsCategoryApplication 和 NewsCategoryController呢?
答案,须要为复杂逻辑预留扩展点。
整个过程,和 NewsCategory 基本一致,在此不在重复,只选择差别点进行说明。 NewsInfo 最终代码以下:
@EnableGenForAggregate
@Index("categoryId")
@Data
@Entity
@Table(name = "tb_news_info")
public class NewsInfo extends JpaAggregate {
@Column(name = "category_id", updatable = false)
private Long categoryId;
@Setter(AccessLevel.PRIVATE)
@Convert(converter = CodeBasedNewsInfoStatusConverter.class)
private NewsInfoStatus status;
private String title;
private String content;
private NewsInfo(){
}
/**
* GenApplicationIgnore 建立 BaseNewsInfoApplication 时,忽略该方法,由于 Optional<NewsCategory> category 须要经过 逻辑进行获取
* @param category
* @param creator
* @return
*/
@GenApplicationIgnore
public static NewsInfo create(Optional<NewsCategory> category, NewsInfoCreator creator){
// 对 NewsCategory 的存在性和状态进行验证
if (!category.isPresent() || category.get().getStatus() != NewsCategoryStatus.ENABLE){
throw new IllegalArgumentException();
}
NewsInfo newsInfo = new NewsInfo();
creator.accept(newsInfo);
newsInfo.init();
return newsInfo;
}
public void update(NewsInfoUpdater updater){
updater.accept(this);
}
public void enable(){
setStatus(NewsInfoStatus.ENABLE);
}
public void disable(){
setStatus(NewsInfoStatus.DISABLE);
}
private void init() {
setStatus(NewsInfoStatus.ENABLE);
}
}
复制代码
NewsInfo 的建立逻辑中,须要对 NewsCategory 的存在性和状态进行检查,只有存在而且状态为 ENABLE 才能添加 NewsInfo。
具体实现以下:
/**
* GenApplicationIgnore 建立 BaseNewsInfoApplication 时,忽略该方法,由于 Optional<NewsCategory> category 须要经过 逻辑进行获取
* @param category
* @param creator
* @return
*/
@GenApplicationIgnore
public static NewsInfo create(Optional<NewsCategory> category, NewsInfoCreator creator){
// 对 NewsCategory 的存在性和状态进行验证
if (!category.isPresent() || category.get().getStatus() != NewsCategoryStatus.ENABLE){
throw new IllegalArgumentException();
}
NewsInfo newsInfo = new NewsInfo();
creator.accept(newsInfo);
newsInfo.init();
return newsInfo;
}
复制代码
该方法比较复杂,须要咱们手工处理。
在 NewsInfoApplication 中手工添加建立方法:
@GenController("com.geekhalo.ddd.lite.demo.controller.BaseNewsInfoController")
public interface NewsInfoApplication extends BaseNewsInfoApplication{
// 手工维护方法
NewsInfo create(Long categoryId, NewsInfoCreator creator);
}
复制代码
在 NewsInfoApplicationImpl 添加实现:
@Autowired
private NewsCategoryRepository newsCategoryRepository;
@Override
public NewsInfo create(Long categoryId, NewsInfoCreator creator) {
return creatorFor(getNewsInfoRepository())
.publishBy(getDomainEventBus())
.instance(()-> NewsInfo.create(this.newsCategoryRepository.getById(categoryId), creator))
.call();
}
复制代码
其余部分不须要调整。
查找逻辑设计两个部分:
在 NewsInfo 类上多了一个 @Index("categoryId") 注解,该注解会在 BaseNewsInfoRepository 中添加以 categoryId 为维度的查询。
interface BaseNewsInfoRepository extends SpringDataRepositoryAdapter<Long, NewsInfo>, Repository<NewsInfo, Long>, QuerydslPredicateExecutor<NewsInfo> {
Long countByCategoryId(Long categoryId);
default Long countByCategoryId(Long categoryId, Predicate predicate) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(QNewsInfo.newsInfo.categoryId.eq(categoryId));;
booleanBuilder.and(predicate);
return this.count(booleanBuilder.getValue());
}
List<NewsInfo> getByCategoryId(Long categoryId);
List<NewsInfo> getByCategoryId(Long categoryId, Sort sort);
default List<NewsInfo> getByCategoryId(Long categoryId, Predicate predicate) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(QNewsInfo.newsInfo.categoryId.eq(categoryId));;
booleanBuilder.and(predicate);
return Lists.newArrayList(findAll(booleanBuilder.getValue()));
}
default List<NewsInfo> getByCategoryId(Long categoryId, Predicate predicate, Sort sort) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(QNewsInfo.newsInfo.categoryId.eq(categoryId));;
booleanBuilder.and(predicate);
return Lists.newArrayList(findAll(booleanBuilder.getValue(), sort));
}
Page<NewsInfo> findByCategoryId(Long categoryId, Pageable pageable);
default Page<NewsInfo> findByCategoryId(Long categoryId, Predicate predicate, Pageable pageable) {
BooleanBuilder booleanBuilder = new BooleanBuilder();
booleanBuilder.and(QNewsInfo.newsInfo.categoryId.eq(categoryId));;
booleanBuilder.and(predicate);
return findAll(booleanBuilder.getValue(), pageable);
}
}
复制代码
这样,并解决了第一个问题。
查看 NewsInfoRepository 类,以下:
@GenApplication
public interface NewsInfoRepository extends BaseNewsInfoRepository{
default Page<NewsInfo> findValidByCategoryId(Long categoryId, Pageable pageable){
// 查找有效状态
Predicate valid = QNewsInfo.newsInfo.status.eq(NewsInfoStatus.ENABLE);
return findByCategoryId(categoryId, valid, pageable);
}
}
复制代码
经过默认方法将业务概念转为为数据过滤。
至此,整个结构与 NewsCategory 再无区别。 最后,咱们添加数据库文件 V1.003__create_news_info.sql :
create table tb_news_info
(
id bigint auto_increment primary key,
category_id bigint not null,
status tinyint null,
title varchar(64) not null,
content text null,
create_time bigint not null,
update_time bigint not null,
version tinyint not null
);
复制代码
启动项目,进行简单测试。
你用了多长时间完成整个系统呢?
项目地址见:gitee.com/litao851025…