摆脱Spring依赖症——使用Vert.x 4与JOOQ打造REST服务脚手架项目

  • Post author:
  • Post category:其他




相关文章



Vert.x-Web与SpringBoot整合





前言


上一次发布关于Vert.x的文章已经是两年多以前了,写《Vert.x-Web与SpringBoot整合》的意图主要是帮助想尝试Vert.x又很难快速从spring框架的开发模式中转变过来的同学,而这一篇文章将完全剥离spring,帮助大家体验纯粹的Vert.x之美。正好Vert.x 4.0大本版已经正式发布,我们就用4.0来撸一个脚手架项目。




一、项目框架



二、项目目录结构

目录结构


项目包含core和web两个模块:core模块为框架核心模块,核心模块不包含任何业务代码,主要有自定义注解、工具、基础类和框架verticle;web模块为网络模块,由于是demo项目,没有再具体划分,因此该模块包括业务代码、数据源相关代码以及持久化层相关框架代码,同时web模块也包含项目的入口。




三、项目构建


项目使用maven构建,JDK版本为1.8。下面贴上pom.xml

  • core
<?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">
    <parent>
        <artifactId>vertx-web-jooq-demo</artifactId>
        <groupId>com.xxx</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>${project.parent.version}</version>
    <artifactId>core</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <vertx.version>4.0.0</vertx.version>
        <org.reflections.version>0.9.12</org.reflections.version>
        <lombok.version>1.18.12</lombok.version>
        <slf4j.version>1.7.25</slf4j.version>
        <commons-lang3.version>3.4</commons-lang3.version>
        <jackson.version>2.11.3</jackson.version>
        <logback.version>1.2.3</logback.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.vertx</groupId>
                <artifactId>vertx-dependencies</artifactId>
                <version>${vertx.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-service-proxy</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-codegen</artifactId>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>${org.reflections.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


在核心模块引入Vert.x相关框架,值得一提的是reflections是非常好用的反射框架,可以帮我们解决批量扫描操作各种业务类的问题。

  • 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">
    <parent>
        <artifactId>vertx-web-jooq-demo</artifactId>
        <groupId>com.xxx</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <version>${project.parent.version}</version>
    <artifactId>web</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <logback.version>1.2.3</logback.version>
        <slf4j.version>1.7.25</slf4j.version>
        <vertx-jooq.version>6.1.0</vertx-jooq.version>
        <hikari-cp.version>3.4.5</hikari-cp.version>
        <mysql-driver.version>5.1.37</mysql-driver.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.xxx</groupId>
            <artifactId>core</artifactId>
            <version>${project.parent.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.github.jklingsporn</groupId>
            <artifactId>vertx-jooq-classic-jdbc</artifactId>
            <version>${vertx-jooq.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.jklingsporn</groupId>
            <artifactId>vertx-jooq-generate</artifactId>
            <version>${vertx-jooq.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-driver.version}</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>${hikari-cp.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <directory>${project.basedir}/target</directory>
        <outputDirectory>${project.build.directory}/classes</outputDirectory>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <annotationProcessors>
                        <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor>
                    </annotationProcessors>
                    <generatedSourcesDirectory>
                        ${project.basedir}/src/main/generated
                    </generatedSourcesDirectory>
                    <compilerArgs>
                        <arg>-AoutputDirectory=${project.basedir}/src/main</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <!--            <plugin>-->
            <!--                <groupId>org.apache.maven.plugins</groupId>-->
            <!--                <artifactId>maven-shade-plugin</artifactId>-->
            <!--                <version>2.3</version>-->
            <!--                <executions>-->
            <!--                    <execution>-->
            <!--                        <phase>package</phase>-->
            <!--                        <goals>-->
            <!--                            <goal>shade</goal>-->
            <!--                        </goals>-->
            <!--                        <configuration>-->
            <!--                            <transformers>-->
            <!--                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
            <!--                                    <manifestEntries>-->
            <!--                                        <Main-Class>io.vertx.core.Launcher</Main-Class>-->
            <!--                                        <Main-Verticle>com.xxx.verticle.MainVerticle</Main-Verticle>-->
            <!--                                    </manifestEntries>-->
            <!--                                </transformer>-->
            <!--                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>-->
            <!--                            </transformers>-->
            <!--                            <artifactSet>-->
            <!--                            </artifactSet>-->
            <!--                            <outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>-->
            <!--                        </configuration>-->
            <!--                    </execution>-->
            <!--                </executions>-->
            <!--            </plugin>-->
            <!--打包jar-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <!--不打包资源文件-->
                    <excludes>
                        <exclude>*.**</exclude>
                        <exclude>*/*.xml</exclude>
                    </excludes>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <!--MANIFEST.MF 中 Class-Path 加入前缀-->
                            <classpathPrefix>lib/</classpathPrefix>
                            <!--jar包不包含唯一版本标识-->
                            <useUniqueVersions>false</useUniqueVersions>
                            <!--指定入口类-->
                            <mainClass>com.xxx.AppMain</mainClass>
                        </manifest>
                        <manifestEntries>
                            <!--MANIFEST.MF 中 Class-Path 加入资源文件目录-->
                            <Class-Path>./resources/</Class-Path>
                        </manifestEntries>
                    </archive>
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
            </plugin>
            <!--拷贝依赖 copy-dependencies-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                ${project.build.directory}/lib/
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!--拷贝资源文件 copy-resources-->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <resources>
                                <resource>
                                    <directory>src/main/resources</directory>
                                </resource>
                            </resources>
                            <outputDirectory>${project.build.directory}/resources</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>


web模块引入了core模块,logback日志框架,HikariCP数据库连接池,以及vertx-jooq持久化框架等。由于个人习惯打包插件引用较多,主要是用于打包后将lib与resources从jar包中剥离,便于打包后的配置修改以及局部更新,如果使用fat-jar那么可以直接使用注释掉的maven-shade-plugin插件,使用io.vertx.core.Launcher作为程序入口还需要自行编写MainVerticle类。




四、代码详解



1.配置


配置文件在web模块的resources/conf中,文件名为config.json,这样配置文件就可以被Vert.x Config自动获取。文件如下

{
  "dev": {
    "server": {
      "port": 8088,
      "contextPath": "/demo"
    },
    "vertx": {
      "eventLoopPoolSize": 2,
      "workerPoolSize": 20,
      "blockedThreadCheckInterval": 999999999,
      "maxEventLoopExecuteTime": 999999999,
      "maxWorkerExecuteTime": 999999999,
      "eventBusOptions": {
        "connectTimeout": 1800000
      }
    },
    "dataSource": {
      "driverClassName": "com.mysql.jdbc.Driver",
      "jdbcUrl": "jdbc:mysql://localhost:3306/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&tinyInt1isBit=false",
      "username": "root",
      "password": "******",
      "maximumPoolSize": 8
    },
    "custom": {
      "asyncServiceInstances": 1,
      "routerLocations": "com.xxx.web.rest",
      "handlerLocations": "com.xxx.web.service",
      "daoLocations": "com.xxx.web.jooq.tables.daos"
    }
  },
  "prod": {
    "server": {
      "port": 8108,
      "contextPath": "/demo"
    },
    "http": {
      "maxWebSocketFrameSize": 1000000
    },
    "dataSource": {
      "driverClassName": "com.mysql.jdbc.Driver",
      "jdbcUrl": "jdbc:mysql://localhost:3306/demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&tinyInt1isBit=false",
      "username": "root",
      "password": "******",
      "maximumPoolSize": 16
    },
    "vertx": {
      "eventLoopPoolSize": 4,
      "workerPoolSize": 40,
      "eventBusOptions": {
        "connectTimeout": 6000
      }
    },
    "custom": {
      "asyncServiceInstances": 1,
      "routerLocations": "com.xxx.web.rest",
      "handlerLocations": "com.xxx.web.service",
      "daoLocations": "com.xxx.web.jooq.tables.daos"
    }
  }
}


dev为开发环境配置,prod为生产环境配置,有更多环境可以自行添加,json文件配置十分灵活没有yml中的一些限制。配置中保留了springboot中的一些习惯,例如server配置下有http服务器端口号和项目根路径配置。vertx配置主要是把各种超时时间延长以便开发时进断点调试,数据源配置不赘述,custom自定义配置中asyncServiceInstances用来控制发布的服务层verticle的实例数,其他为reflections的扫描包路径。



2.入口


程序入口是web模块下的AppMain类

package com.xxx.web;

import com.xxx.core.handlerfactory.RouterHandlerFactory;
import com.xxx.core.util.DeployVertxServer;
import com.xxx.core.util.VertxHolder;
import com.xxx.web.dao.DaoConfigurationHolder;
import com.xxx.web.dao.JooqDaoHolder;
import com.xxx.web.datasource.DataSourceHolder;
import io.vertx.config.ConfigRetriever;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.core.shareddata.SharedData;
import io.vertx.ext.web.Router;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * 程序入口
 */
public class AppMain {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppMain.class);

    private static String env = "dev";

    public static void main(String[] args) {
        if (args.length > 0) {
            env = args[0];
        }
        Vertx tempVertx = Vertx.vertx();
        ConfigRetriever retriever = ConfigRetriever.create(tempVertx);
        //获取conf/config.json中的配置
        retriever.getConfig(ar -> {
            tempVertx.close();
            JsonObject result = ar.result();
            LOGGER.info("配置读取成功:" + result.encode());
            //默认读取dev开发环境配置
            JsonObject envConfig = result.getJsonObject(env);
            JsonObject serverConfig = envConfig.getJsonObject("server");
            JsonObject vertxConfig = envConfig.getJsonObject("vertx");
            JsonObject dataSourceConfig = envConfig.getJsonObject("dataSource");
            JsonObject customConfig = envConfig.getJsonObject("custom");
            Vertx vertx = Vertx.vertx(new VertxOptions(vertxConfig));
            VertxHolder.init(vertx);
            //配置保存在共享数据中
            SharedData sharedData = vertx.sharedData();
            LocalMap<String, Object> localMap = sharedData.getLocalMap("demo");
            localMap.put("env", env);
            localMap.put("envConfig", envConfig);
            //先初始化再发布Http服务
            vertx.executeBlocking(p -> {
                //顺序不能乱
                try {
                    //初始化数据源
                    DataSourceHolder.init(dataSourceConfig);
                    //初始化jooq dao配置
                    DaoConfigurationHolder.init();
                    //初始化dao
                    JooqDaoHolder.init(customConfig.getString("daoLocations"));
                    p.complete();
                } catch (Exception e) {
                    p.fail(e);
                }
            }).onComplete(ar2 -> {
                if (ar2.succeeded()) {
                    Router router = new RouterHandlerFactory(customConfig.getString("routerLocations"), serverConfig.getString("contextPath")).createRouter();
                    DeployVertxServer.startDeploy(router, customConfig.getString("handlerLocations"), serverConfig.getInteger("port"),
                            customConfig.getInteger("asyncServiceInstances"));
                } else {
                    LOGGER.error(ar.cause().getMessage(), ar.cause());
                }
            });
        });
    }
}


环境变量env默认为dev,可以通过java启动参数设置修改。

创建临时Vertx 读取config.json文件,然后销毁,再根据配置创建新的Vertx实例,并保存在VertxHolder中便于后续获取。然后将配置信息以及当前运行环境保存在SharedData中便于后续使用。

根据配置文件初始化数据源、jooq的daoConfiguration以及自定义的JooqDaoHolder,初始化成功后,对api进行扫描并构建Router对象,然后根据配置发布http服务verticle以及将服务层注册到EventBus。



3.代码生成


代码生成类JooqGenerator在web模块的test中,vertx-jooq框架生成的代码包括实体类和dao层代码等,具体可以在文章末尾下载源码查看或直接去github搜索vertx-jooq。



4.业务代码


在本框架下,需要开发者自行编写的代码主要是web层与service层代码。web层代码示例如下

package com.xxx.web.rest;

import com.xxx.core.annotaions.RouteHandler;
import com.xxx.core.annotaions.RouteMapping;
import com.xxx.core.annotaions.RouteMethod;
import com.xxx.core.base.BaseRestApi;
import com.xxx.core.model.JsonResult;
import com.xxx.core.util.AsyncServiceUtil;
import com.xxx.core.util.ParamUtil;
import com.xxx.web.service.OrderService;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import org.apache.commons.lang3.StringUtils;

@RouteHandler(value = "orderApi")
public class OrderApi extends BaseRestApi {

    private OrderService orderService = AsyncServiceUtil.getAsyncServiceInstance(OrderService.class);

    @RouteMapping(value = "/findOrderById/:orderId", method = RouteMethod.GET)
    public Handler<RoutingContext> findOrderById() {
        return ctx -> {
            String orderId = ctx.pathParam("orderId");
            if (StringUtils.isBlank(orderId)) {
                sendError(400, ctx);
            } else {
                orderService.findOrderAndItemById(Long.valueOf(orderId), ar -> {
                    if (ar.succeeded()) {
                        JsonObject product = ar.result();
                        fireJsonResponse(ctx, new JsonResult(product));
                    } else {
                        fireErrorJsonResponse(ctx, ar.cause().getMessage());
                    }
                });
            }
        };
    }

    @RouteMapping(value = "/listOrderPage/:current", method = RouteMethod.GET)
    public Handler<RoutingContext> listOrderPage() {
        return ctx -> {
            String current = ctx.pathParam("current");
            if (StringUtils.isBlank(current)) {
                sendError(400, ctx);
            } else {
                orderService.listOrderPage(Integer.valueOf(current), ar -> {
                    if (ar.succeeded()) {
                        fireJsonResponse(ctx, new JsonResult(ar.result()));
                    } else {
                        fireErrorJsonResponse(ctx, ar.cause().getMessage());
                    }
                });
            }
        };
    }

    @RouteMapping(value = "/deleteById/:orderId", method = RouteMethod.DELETE)
    public Handler<RoutingContext> deleteById() {
        return ctx -> {
            String orderId = ctx.pathParam("orderId");
            if (StringUtils.isBlank(orderId)) {
                sendError(400, ctx);
            } else {
                orderService.deleteOrder(Long.valueOf(orderId), ar -> {
                    if (ar.succeeded()) {
                        fireJsonResponse(ctx, new JsonResult(ar.result()));
                    } else {
                        fireErrorJsonResponse(ctx, ar.cause().getMessage());
                    }
                });
            }
        };
    }

    @RouteMapping(value = "/saveOrder", method = RouteMethod.POST)
    public Handler<RoutingContext> saveOrder() {
        return ctx -> {
            JsonObject params = ParamUtil.getRequestParams(ctx);
            orderService.saveOrder(params, ar -> {
                if (ar.succeeded()) {
                    fireJsonResponse(ctx, new JsonResult(ar.result()));
                } else {
                    fireErrorJsonResponse(ctx, ar.cause().getMessage());
                }
            });
        };
    }
}


web层代码乍看之下和SpringMVC极为相似,颇有些亲切感。框架会根据routerLocations配置扫描@RouteHandler和@RouteMapping注解并构建Router对象。该示例演示了CRUD的REST服务写法。


service层代码包括接口和实现类

package com.xxx.web.service;

import io.vertx.codegen.annotations.ProxyGen;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;

/**
 * @author Ian
 * @date 2021/1/11 9:25
 */
@ProxyGen
public interface OrderService {

    void findOrderAndItemById(Long orderId, Handler<AsyncResult<JsonObject>> resultHandler);

    void listOrderPage(Integer current, Handler<AsyncResult<JsonObject>> resultHandler);

    void deleteOrder(Long orderId, Handler<AsyncResult<Void>> resultHandler);

    void saveOrder(JsonObject json, Handler<AsyncResult<JsonObject>> resultHandler);
}
package com.xxx.web.service.impl;

import com.xxx.core.base.BaseAsyncService;
import com.xxx.core.util.IdWorker;
import com.xxx.web.dao.JooqDaoHolder;
import com.xxx.web.jooq.tables.daos.TOrderDao;
import com.xxx.web.jooq.tables.daos.TOrderItemDao;
import com.xxx.web.jooq.tables.pojos.TOrder;
import com.xxx.web.jooq.tables.pojos.TOrderItem;
import com.xxx.web.jooq.tables.records.TOrderRecord;
import com.xxx.web.service.OrderService;
import com.xxx.web.util.PojoUtil;
import io.vertx.core.AsyncResult;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import org.jooq.SortField;
import org.jooq.UpdateQuery;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author Ian
 * @date 2021/1/11 9:55
 */
public class OrderServiceImpl extends BaseAsyncService implements OrderService {

    private TOrderDao orderDao = JooqDaoHolder.getDaoInstance(TOrderDao.class);

    private TOrderItemDao orderItemDao = JooqDaoHolder.getDaoInstance(TOrderItemDao.class);

    @Override
    public void findOrderAndItemById(Long orderId, Handler<AsyncResult<JsonObject>> resultHandler) {
        Future<TOrder> future1 = orderDao.findOneById(orderId);
        Future<List<TOrderItem>> future2 = orderItemDao.findManyByOrderId(Collections.singleton(orderId));
        CompositeFuture.all(future1, future2).onSuccess(ar -> {
            if (ar.succeeded()) {
                TOrder tOrder = ar.resultAt(0);
                if (null == tOrder) {
                    resultHandler.handle(Future.succeededFuture(null));
                } else {
                    JsonObject result = tOrder.toJson();
                    List<TOrderItem> tOrderItemList = ar.resultAt(1);
                    result.put("children", PojoUtil.convertToArray(tOrderItemList));
                    resultHandler.handle(Future.succeededFuture(result));
                }
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }

    @Override
    public void listOrderPage(Integer current, Handler<AsyncResult<JsonObject>> resultHandler) {
        SortField<?> sortField = com.xxx.web.jooq.tables.TOrder.T_ORDER.CREATE_TIME.desc();
        JsonObject page = new JsonObject();
        page.put("current", current);
        page.put("size", 10);
        int offset = (current - 1) * 10;
        orderDao.queryExecutor().query(dslContext ->
                dslContext.selectCount().from(com.xxx.web.jooq.tables.TOrder.T_ORDER)
        ).compose(queryResult -> {
            page.put("total", queryResult.get(0, int.class));
            return orderDao.findManyByCondition(null, 10, offset, sortField);
        }).onComplete(ar -> {
            if (ar.succeeded()) {
                page.put("rows", PojoUtil.convertToArray(ar.result()));
                resultHandler.handle(Future.succeededFuture(page));
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }

    @Override
    public void deleteOrder(Long orderId, Handler<AsyncResult<Void>> resultHandler) {
        //无事务
//        orderDao.deleteById(orderId).compose(integer ->
//                orderItemDao.deleteByCondition(com.xxx.web.jooq.tables.TOrderItem.T_ORDER_ITEM.ORDER_ID.eq(orderId))
//        ).onComplete(ar -> {
//            if (ar.succeeded()) {
//                resultHandler.handle(Future.succeededFuture());
//            } else {
//                handleException(ar.cause(), resultHandler);
//            }
//        });
        //有事务
        orderDao.queryExecutor().executeAny(dslContext -> {
                    dslContext.transaction(c -> {
                        c.dsl().deleteFrom(com.xxx.web.jooq.tables.TOrder.T_ORDER)
                                .where(com.xxx.web.jooq.tables.TOrder.T_ORDER.ID.eq(orderId)).execute();
                        c.dsl().deleteFrom(com.xxx.web.jooq.tables.TOrderItem.T_ORDER_ITEM)
                                .where(com.xxx.web.jooq.tables.TOrderItem.T_ORDER_ITEM.ORDER_ID.eq(orderId)).execute();
                    });
                    return 0;
                }
        ).onComplete(ar -> {
            if (ar.succeeded()) {
                resultHandler.handle(Future.succeededFuture());
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }

    @Override
    public void saveOrder(JsonObject json, Handler<AsyncResult<JsonObject>> resultHandler) {
        TOrder tOrder = new TOrder(json);
        Future<Integer> future;
        if (json.containsKey("id") && null != json.getValue("id")) {
            future = orderDao.queryExecutor().executeAny(dslContext -> {
                        UpdateQuery<TOrderRecord> updateQuery = dslContext.updateQuery(com.xxx.web.jooq.tables.TOrder.T_ORDER);
                        Map<String, Object> map = new HashMap<>();
                        if (null != tOrder.getName()) {
                            map.put("name", tOrder.getName());
                        }
                        if (null != tOrder.getUserName()) {
                            map.put("user_name", tOrder.getUserName());
                        }
                        updateQuery.addValues(map);
                        updateQuery.addConditions(com.xxx.web.jooq.tables.TOrder.T_ORDER.ID.eq(tOrder.getId()));
                        return updateQuery.execute();
                    }
            );
            //修改所有字段
//            future = orderDao.update(tOrder);
        } else {
            tOrder.setId(IdWorker.getId()).setCreateTime(LocalDateTime.now());
            future = orderDao.insert(tOrder);
        }
        future.onComplete(ar -> {
            if (ar.succeeded()) {
                resultHandler.handle(Future.succeededFuture(tOrder.toJson()));
            } else {
                handleException(ar.cause(), resultHandler);
            }
        });
    }
}


需要注意的有两点:接口需要加上@ProxyGen注解,这样Vert.x Service Proxy框架就会自动生成服务代理类(注意在入口类同级添加package-info.java);实现类需要继承BaseAsyncService类,这样才能被扫描到并注册到EventBus。




总结


异步之于Vert.x既是优点也是缺点,在高性能之下,对开发者编程能力也有着比同步编程更高的要求,希望本文能够对想要跳出spring体系看看外面的世界的同学们有所帮助,下面是源码,记得star哦!


源码下载



版权声明:本文为u013615903原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。