我是 Helidon 的新手,我正在尝试使用 Helidon SE 创建一个基本的 CRUD REST 服务。我一直在参考 GitHib ( https://github.com/oracle/helidon/tree/master/examples/dbclient ) 中的 DbClient 示例来创建基本的 CRUD REST 服务。
我能够在 DB 中执行 Read all/one 和 Delete all/one 但无法执行创建或更新操作,下面是我在尝试调用 POST 服务时得到的错误堆栈:
java.util.concurrent.ExecutionException: Unhandled 'cause' of this exception encountered.
at io.helidon.webserver.RequestRouting$RoutedRequest.defaultHandler(RequestRouting.java:394)
at io.helidon.webserver.RequestRouting$RoutedRequest.nextNoCheck(RequestRouting.java:374)
at io.helidon.webserver.RequestRouting$RoutedRequest.next(RequestRouting.java:417)
at io.helidon.webserver.Handler.lambda$create$4(Handler.java:99)
at java.base/java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:986)
at java.base/java.util.concurrent.CompletableFuture.uniExceptionallyStage(CompletableFuture.java:1004)
at java.base/java.util.concurrent.CompletableFuture.exceptionally(CompletableFuture.java:2307)
at java.base/java.util.concurrent.CompletableFuture.exceptionally(CompletableFuture.java:143)
at io.helidon.common.reactive.CompletionAwaitable.exceptionally(CompletionAwaitable.java:293)
at io.helidon.webserver.Handler.lambda$create$5(Handler.java:97)
at io.helidon.webserver.RequestRouting$RoutedRequest.next(RequestRouting.java:320)
at io.helidon.metrics.MetricsSupport$MetricsContextHandler.accept(MetricsSupport.java:619)
at io.helidon.webserver.RequestRouting$RoutedRequest.next(RequestRouting.java:320)
at io.helidon.metrics.MetricsSupport.lambda$configureVendorMetrics$7(MetricsSupport.java:364)
at io.helidon.webserver.RequestRouting$RoutedRequest.next(RequestRouting.java:320)
at io.helidon.webserver.WebTracingConfig$RequestSpanHandler.accept(WebTracingConfig.java:247)
at io.helidon.webserver.RequestRouting$RoutedRequest.next(RequestRouting.java:320)
at io.helidon.common.context.Contexts.runInContext(Contexts.java:98)
at io.helidon.webserver.RequestRouting.route(RequestRouting.java:87)
at io.helidon.webserver.ForwardingHandler.channelRead0(ForwardingHandler.java:167)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:311)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:425)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: No reader found for type: class io.helidon.examples.quickstart.se.pokemon.Pokemon
at io.helidon.media.common.MessageBodyReaderContext.readerNotFound(MessageBodyReaderContext.java:338)
at io.helidon.media.common.MessageBodyReaderContext.unmarshall(MessageBodyReaderContext.java:167)
at io.helidon.media.common.MessageBodyReadableContent.as(MessageBodyReadableContent.java:117)
at io.helidon.webserver.Handler.lambda$create$5(Handler.java:83)
... 34 more
下面是写的代码
主.java
package io.helidon.examples.quickstart.se;
import io.helidon.config.Config;
import io.helidon.config.ConfigValue;
import io.helidon.dbclient.DbClient;
import io.helidon.examples.quickstart.se.pokemon.PokemonService;
import io.helidon.health.HealthSupport;
import io.helidon.health.checks.HealthChecks;
import io.helidon.media.jsonp.JsonpSupport;
import io.helidon.metrics.MetricsSupport;
import io.helidon.webserver.Routing;
import io.helidon.webserver.WebServer;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.logging.LogManager;
/**
* The application main class.
*/
public final class Main {
/**
* Cannot be instantiated.
*/
private Main() {
}
/**
* Application main entry point.
* @param args command line arguments.
* @throws IOException if there are problems reading logging properties
*/
public static void main(final String[] args) throws IOException {
startServer();
}
/**
* Start the server.
* @return the created {@link WebServer} instance
* @throws IOException if there are problems reading logging properties
*/
static WebServer startServer() throws IOException {
// load logging configuration
setupLogging();
System.out.println("Logging Set up");
// By default this will pick up application.yaml from the classpath
Config config = Config.create();
System.out.println("Config created");
// Build server with JSONP support
WebServer server = WebServer.builder(createRouting(config))
.config(config.get("server"))
.addMediaSupport(JsonpSupport.create())
.build();
System.out.println("Webserver Created : "+server);
// Try to start the server. If successful, print some info and arrange to
// print a message at shutdown. If unsuccessful, print the exception.
System.out.println("Server startup initiating");
server.start()
.thenAccept(ws -> {
System.out.println(
"WEB server is up! http://localhost:" + ws.port() + "/greet");
ws.whenShutdown().thenRun(()
-> System.out.println("WEB server is DOWN. Good bye!"));
})
.exceptionally(t -> {
System.err.println("Startup failed: " + t.getMessage());
t.printStackTrace(System.err);
return null;
});
// Server threads are not daemon. No need to block. Just react.
return server;
}
/**
* Creates new {@link Routing}.
*
* @return routing configured with JSON support, a health check, and a service
* @param config configuration of this server
*/
private static Routing createRouting(Config config) {
System.out.println("Inside create Routing.");
Config dbConfig = config.get("db");
System.out.println("dbConfig : ");
System.out.println(dbConfig.get("db.source").asString().orElse("No Data"));
ConfigValue<Map<String, String>> test = dbConfig.asMap();
// for (Map.Entry<String,String> entry : test)
// System.out.println("Key = " + entry.getKey() +
// ", Value = " + entry.getValue());
//Client services are added through a service loader
DbClient dbClient = DbClient.builder(dbConfig).build();
System.out.println("dbClient : "+dbClient);
MetricsSupport metrics = MetricsSupport.create();
GreetService greetService = new GreetService(config);
PokemonService pokemonService = new PokemonService(dbClient);
HealthSupport health = HealthSupport.builder()
.addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks
.build();
System.out.println("Returning Value");
return Routing.builder()
.register(health) // Health at "/health"
.register(metrics) // Metrics at "/metrics"
.register("/greet", greetService)
.register("/pokemon", pokemonService)
.build();
}
/**
* Configure logging from logging.properties file.
*/
private static void setupLogging() throws IOException {
try (InputStream is = Main.class.getResourceAsStream("/logging.properties")) {
LogManager.getLogManager().readConfiguration(is);
}
}
}
PokemonService.java
package io.helidon.examples.quickstart.se.pokemon;
import io.helidon.common.http.Http;
import io.helidon.common.reactive.Multi;
import io.helidon.dbclient.DbClient;
import io.helidon.dbclient.DbRow;
import io.helidon.webserver.*;
import javax.json.JsonObject;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PokemonService implements Service {
private static final Logger LOGGER = Logger.getLogger(PokemonService.class.getName());
private final AtomicReference<String> greeting = new AtomicReference<>();
private final DbClient dbClient;
public PokemonService(DbClient dbClient){
this.dbClient = dbClient;
greeting.set("Hello Pokemon");
}
@Override
public void update(Routing.Rules rules) {
rules
.get("/", this::listPokemons)
// get one
.get("/{name}", this::getPokemon)
// create new
.post("/", Handler.create(Pokemon.class, this::insertPokemon))
// delete all
.delete("/", this::deleteAllPokemons)
// delete one
.delete("/{name}", this::deletePokemon)
;
}
private void listPokemons(ServerRequest request, ServerResponse response) {
Multi<JsonObject> rows = dbClient.execute(exec -> exec.namedQuery("select-all"))
.map(it -> it.as(JsonObject.class));
response.send(rows, JsonObject.class);
}
private void insertPokemon(ServerRequest request, ServerResponse response, Pokemon pokemon) {
System.out.println("Start of insertPokemon");
System.out.println("pokemon : ");
System.out.println(pokemon);
System.out.println("Name : "+pokemon.getName());
System.out.println("Type : "+pokemon.getType());
dbClient.execute(exec -> exec
.createNamedInsert("insert2")
.namedParam(pokemon)
.execute())
.thenAccept(count -> response.send("Inserted: " + count + " values"))
.exceptionally(throwable -> sendError(throwable, response));
}
private <T> T sendError(Throwable throwable, ServerResponse response) {
Throwable realCause = throwable;
if (throwable instanceof CompletionException) {
realCause = throwable.getCause();
}
response.status(Http.Status.INTERNAL_SERVER_ERROR_500);
response.send("Failed to process request: " + realCause.getClass().getName() + "(" + realCause.getMessage() + ")");
LOGGER.log(Level.WARNING, "Failed to process request", throwable);
return null;
}
private void getPokemon(ServerRequest request, ServerResponse response) {
String pokemonName = request.path().param("name");
dbClient.execute(exec -> exec.namedGet("select-one", pokemonName))
.thenAccept(opt -> opt.ifPresentOrElse(it -> sendRow(it, response),
() -> sendNotFound(response, "Pokemon "
+ pokemonName
+ " not found")))
.exceptionally(throwable -> sendError(throwable, response));
}
private void sendRow(DbRow row, ServerResponse response) {
response.send(row.as(JsonObject.class));
}
private void sendNotFound(ServerResponse response, String message) {
response.status(Http.Status.NOT_FOUND_404);
response.send(message);
}
private void deleteAllPokemons(ServerRequest request, ServerResponse response) {
dbClient.execute(exec -> exec
// this is to show how ad-hoc statements can be executed (and their naming in Tracing and Metrics)
.createDelete("DELETE FROM pokemons")
.execute())
.thenAccept(count -> response.send("Deleted: " + count + " values"))
.exceptionally(throwable -> sendError(throwable, response));
}
private void deletePokemon(ServerRequest request, ServerResponse response) {
final String name = request.path().param("name");
dbClient.execute(exec -> exec.namedDelete("delete", name))
.thenAccept(count -> response.send("Deleted: " + count + " values"))
.exceptionally(throwable -> sendError(throwable, response));
}
}
口袋妖怪.java
package io.helidon.examples.quickstart.se.pokemon;
import io.helidon.common.Reflected;
@Reflected
public class Pokemon {
private String name;
private String type;
/**
* Default constructor.
*/
public Pokemon() {
// JSON-B
}
/**
* Create pokemon with name and type.
*
* @param name name of the beast
* @param type type of the beast
*/
public Pokemon(String name, String type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
PokemonMapper.java
package io.helidon.examples.quickstart.se.pokemon;
import io.helidon.dbclient.DbColumn;
import io.helidon.dbclient.DbMapper;
import io.helidon.dbclient.DbRow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Maps database statements to {@link io.helidon.examples.quickstart.se.pokemon.Pokemon} class.
*/
public class PokemonMapper implements DbMapper<Pokemon> {
@Override
public Pokemon read(DbRow row) {
// DbColumn id = row.column("id");
DbColumn name = row.column("name");
DbColumn type = row.column("type");
return new Pokemon(name.as(String.class), type.as(String.class));
}
@Override
public Map<String, Object> toNamedParameters(Pokemon value) {
Map<String, Object> map = new HashMap<>(2);
// map.put("id", value.getId());
map.put("name", value.getName());
map.put("type", value.getType());
return map;
}
@Override
public List<Object> toIndexedParameters(Pokemon value) {
List<Object> list = new ArrayList<>(2);
// list.add(value.getId());
list.add(value.getName());
list.add(value.getType());
return list;
}
}
PokemonMapperProvider.java
package io.helidon.examples.quickstart.se.pokemon;
import io.helidon.dbclient.DbMapper;
import io.helidon.dbclient.spi.DbMapperProvider;
import javax.annotation.Priority;
import java.util.Optional;
/**
* Provides pokemon mappers.
*/
@Priority(1000)
public class PokemonMapperProvider implements DbMapperProvider {
private static final PokemonMapper MAPPER = new PokemonMapper();
@SuppressWarnings("unchecked")
@Override
public <T> Optional<DbMapper<T>> mapper(Class<T> type) {
if (type.equals(Pokemon.class)) {
return Optional.of((DbMapper<T>) MAPPER);
}
return Optional.empty();
}
}
非常感谢这方面的任何帮助。
问候, 高拉夫