mirror of
https://github.com/jlengrand/quarkus.git
synced 2026-03-10 08:41:22 +00:00
@@ -510,6 +510,16 @@ public class CodecFruitService {
|
||||
}
|
||||
----
|
||||
|
||||
== The POJO Codec
|
||||
|
||||
The link:http://mongodb.github.io/mongo-java-driver/3.12/bson/pojos[POJO Codec] provides a set of annotations that enable the customization of
|
||||
the way a POJO is mapped to a MongoDB collection and this codec is initialized automatically by Quarkus
|
||||
|
||||
One of these annotations is the `@BsonDiscriminator` annotation that allows to storage multiple Java types in a single MongoDB collection by adding
|
||||
a discriminator field inside the document. It can be useful when working with abstract types or interfaces.
|
||||
|
||||
Quarkus will automatically register all the classes annotated with `@BsonDiscriminator` with the POJO codec.
|
||||
|
||||
== Simplifying MongoDB with Panache
|
||||
|
||||
The link:mongodb-panache[MongoDB with Panache] extension facilitates the usage of MongoDB by providing active record style entities (and repositories) like you have in link:hibernate-orm-panache.html[Hibernate ORM with Panache] and focuses on making your entities trivial and fun to write in Quarkus.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.quarkus.mongodb.deployment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.quarkus.builder.item.SimpleBuildItem;
|
||||
|
||||
public final class BsonDiscriminatorBuildItem extends SimpleBuildItem {
|
||||
|
||||
private List<String> bsonDisciminatorClassNames;
|
||||
|
||||
public BsonDiscriminatorBuildItem(List<String> bsonDisciminatorClassNames) {
|
||||
this.bsonDisciminatorClassNames = bsonDisciminatorClassNames;
|
||||
}
|
||||
|
||||
public List<String> getBsonDisciminatorClassNames() {
|
||||
return bsonDisciminatorClassNames;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package io.quarkus.mongodb.deployment;
|
||||
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
|
||||
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -15,6 +16,7 @@ import javax.enterprise.inject.Default;
|
||||
import javax.enterprise.inject.Produces;
|
||||
|
||||
import org.bson.codecs.configuration.CodecProvider;
|
||||
import org.bson.codecs.pojo.annotations.BsonDiscriminator;
|
||||
import org.jboss.jandex.AnnotationInstance;
|
||||
import org.jboss.jandex.AnnotationValue;
|
||||
import org.jboss.jandex.ClassInfo;
|
||||
@@ -72,8 +74,10 @@ public class MongoClientProcessor {
|
||||
@BuildStep
|
||||
void configureRuntimeProperties(MongoClientRecorder recorder,
|
||||
CodecProviderBuildItem codecProvider,
|
||||
BsonDiscriminatorBuildItem bsonDiscriminator,
|
||||
MongodbConfig config) {
|
||||
recorder.configureRuntimeProperties(codecProvider.getCodecProviderClassNames(), config);
|
||||
recorder.configureRuntimeProperties(codecProvider.getCodecProviderClassNames(),
|
||||
bsonDiscriminator.getBsonDisciminatorClassNames(), config);
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
@@ -85,8 +89,23 @@ public class MongoClientProcessor {
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
List<ReflectiveClassBuildItem> addCodecsToNative(CodecProviderBuildItem providers) {
|
||||
return providers.getCodecProviderClassNames().stream()
|
||||
BsonDiscriminatorBuildItem collectBsonDiscriminators(CombinedIndexBuildItem indexBuildItem) {
|
||||
List<String> names = new ArrayList<>();
|
||||
DotName bsonDiscriminatorName = DotName.createSimple(BsonDiscriminator.class.getName());
|
||||
for (AnnotationInstance annotationInstance : indexBuildItem.getIndex().getAnnotations(bsonDiscriminatorName)) {
|
||||
names.add(annotationInstance.target().asClass().name().toString());
|
||||
}
|
||||
return new BsonDiscriminatorBuildItem(names);
|
||||
}
|
||||
|
||||
@BuildStep
|
||||
List<ReflectiveClassBuildItem> addCodecsAndDiscriminatorsToNative(CodecProviderBuildItem codecProviders,
|
||||
BsonDiscriminatorBuildItem bsonDiscriminators) {
|
||||
List<String> reflectiveClassNames = new ArrayList<>();
|
||||
reflectiveClassNames.addAll(codecProviders.getCodecProviderClassNames());
|
||||
reflectiveClassNames.addAll(bsonDiscriminators.getBsonDisciminatorClassNames());
|
||||
|
||||
return reflectiveClassNames.stream()
|
||||
.map(s -> new ReflectiveClassBuildItem(true, true, false, s))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import javax.annotation.PreDestroy;
|
||||
import org.bson.codecs.configuration.CodecProvider;
|
||||
import org.bson.codecs.configuration.CodecRegistries;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
import org.bson.codecs.pojo.ClassModel;
|
||||
import org.bson.codecs.pojo.Conventions;
|
||||
import org.bson.codecs.pojo.PojoCodecProvider;
|
||||
import org.jboss.logging.Logger;
|
||||
@@ -54,6 +55,7 @@ public abstract class AbstractMongoClientProducer {
|
||||
private MongodbConfig mongodbConfig;
|
||||
private boolean disableSslSupport = false;
|
||||
private List<String> codecProviders;
|
||||
private List<String> bsonDiscriminators;
|
||||
private Map<String, MongoClient> mongoclients = new HashMap<>();
|
||||
private Map<String, ReactiveMongoClient> reactiveMongoClients = new HashMap<>();
|
||||
|
||||
@@ -217,11 +219,21 @@ public abstract class AbstractMongoClientProducer {
|
||||
}
|
||||
// add pojo codec provider with automatic capabilities
|
||||
// it always needs to be the last codec provided
|
||||
CodecProvider pojoCodecProvider = PojoCodecProvider.builder()
|
||||
PojoCodecProvider.Builder pojoCodecProviderBuilder = PojoCodecProvider.builder()
|
||||
.automatic(true)
|
||||
.conventions(Conventions.DEFAULT_CONVENTIONS)
|
||||
.build();
|
||||
providers.add(pojoCodecProvider);
|
||||
.conventions(Conventions.DEFAULT_CONVENTIONS);
|
||||
// register bson discriminators
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
for (String bsonDiscriminator : bsonDiscriminators) {
|
||||
try {
|
||||
pojoCodecProviderBuilder
|
||||
.register(ClassModel.builder(Class.forName(bsonDiscriminator, true, classLoader))
|
||||
.enableDiscriminator(true).build());
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
providers.add(pojoCodecProviderBuilder.build());
|
||||
CodecRegistry registry = CodecRegistries.fromRegistries(defaultCodecRegistry,
|
||||
CodecRegistries.fromProviders(providers));
|
||||
settings.codecRegistry(registry);
|
||||
@@ -367,6 +379,10 @@ public abstract class AbstractMongoClientProducer {
|
||||
this.codecProviders = codecs;
|
||||
}
|
||||
|
||||
public void setBsonDiscriminators(List<String> bsonDiscriminators) {
|
||||
this.bsonDiscriminators = bsonDiscriminators;
|
||||
}
|
||||
|
||||
public void disableSslSupport() {
|
||||
this.disableSslSupport = true;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class MongoClientRecorder {
|
||||
};
|
||||
}
|
||||
|
||||
public void configureRuntimeProperties(List<String> codecs, MongodbConfig config) {
|
||||
public void configureRuntimeProperties(List<String> codecs, List<String> bsonDiscriminators, MongodbConfig config) {
|
||||
// TODO @dmlloyd
|
||||
// Same here, the map is entirely empty (obviously, I didn't expect the values
|
||||
// that were not properly injected but at least the config objects present in
|
||||
@@ -38,6 +38,7 @@ public class MongoClientRecorder {
|
||||
// The elements from the default mongoClient are there
|
||||
AbstractMongoClientProducer producer = Arc.container().instance(AbstractMongoClientProducer.class).get();
|
||||
producer.setCodecs(codecs);
|
||||
producer.setBsonDiscriminators(bsonDiscriminators);
|
||||
producer.setConfig(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.quarkus.it.mongodb.discriminator;
|
||||
|
||||
import org.bson.codecs.pojo.annotations.BsonDiscriminator;
|
||||
|
||||
@BsonDiscriminator(key = "type", value = "CAR")
|
||||
public class Car extends Vehicle {
|
||||
public int seatNumber;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.quarkus.it.mongodb.discriminator;
|
||||
|
||||
import org.bson.codecs.pojo.annotations.BsonDiscriminator;
|
||||
|
||||
@BsonDiscriminator(key = "type", value = "MOTO")
|
||||
public class Moto extends Vehicle {
|
||||
public boolean sideCar;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.quarkus.it.mongodb.discriminator;
|
||||
|
||||
import org.bson.codecs.pojo.annotations.BsonDiscriminator;
|
||||
|
||||
@BsonDiscriminator(key = "type")
|
||||
public abstract class Vehicle {
|
||||
public String type;
|
||||
public String name;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.quarkus.it.mongodb.discriminator;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import com.mongodb.client.FindIterable;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
|
||||
@Path("/vehicles")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public class VehicleResource {
|
||||
@Inject
|
||||
MongoClient client;
|
||||
|
||||
private MongoCollection<Vehicle> collection;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
MongoDatabase database = client.getDatabase("books");
|
||||
collection = database.getCollection("vehicle", Vehicle.class);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
public List<Vehicle> getVehicles() {
|
||||
FindIterable<Vehicle> iterable = collection.find();
|
||||
List<Vehicle> vehicles = new ArrayList<>();
|
||||
for (Vehicle doc : iterable) {
|
||||
vehicles.add(doc);
|
||||
}
|
||||
return vehicles;
|
||||
}
|
||||
|
||||
@POST
|
||||
public Response addVehicle(Vehicle vehicle) throws UnsupportedEncodingException {
|
||||
collection.insertOne(vehicle);
|
||||
return Response.created(URI.create("/vehicle/" + URLEncoder.encode(vehicle.name, StandardCharsets.UTF_8.toString())))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.quarkus.it.mongodb.discriminator.jsonb;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import javax.json.bind.JsonbConfig;
|
||||
|
||||
import io.quarkus.jsonb.JsonbConfigCustomizer;
|
||||
|
||||
@Singleton
|
||||
public class VehicleCustomizer implements JsonbConfigCustomizer {
|
||||
@Override
|
||||
public void customize(JsonbConfig jsonbConfig) {
|
||||
jsonbConfig.withDeserializers(new VehicleDeserializer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.quarkus.it.mongodb.discriminator.jsonb;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.bind.serializer.DeserializationContext;
|
||||
import javax.json.bind.serializer.JsonbDeserializer;
|
||||
import javax.json.stream.JsonParser;
|
||||
|
||||
import io.quarkus.it.mongodb.discriminator.Car;
|
||||
import io.quarkus.it.mongodb.discriminator.Moto;
|
||||
import io.quarkus.it.mongodb.discriminator.Vehicle;
|
||||
|
||||
public class VehicleDeserializer implements JsonbDeserializer<Vehicle> {
|
||||
@Override
|
||||
public Vehicle deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
|
||||
JsonObject json = parser.getObject();
|
||||
String type = json.getString("type");
|
||||
switch (type) {
|
||||
case "CAR":
|
||||
Car car = new Car();
|
||||
car.type = type;
|
||||
car.seatNumber = json.getInt("seatNumber");
|
||||
car.name = json.getString("name");
|
||||
return car;
|
||||
case "MOTO":
|
||||
Moto moto = new Moto();
|
||||
moto.type = type;
|
||||
moto.name = json.getString("name");
|
||||
moto.sideCar = json.getBoolean("sideCar");
|
||||
return moto;
|
||||
default:
|
||||
throw new RuntimeException("Type " + type + "not managed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.quarkus.it.mongodb.discriminator.Car;
|
||||
import io.quarkus.it.mongodb.discriminator.Moto;
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
@@ -123,4 +125,24 @@ public class BookResourceTest {
|
||||
"checks.name", containsInAnyOrder("MongoDB connection health check"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVehicleEndpoint() {
|
||||
Car car = new Car();
|
||||
car.name = "Renault Clio";
|
||||
car.type = "CAR";
|
||||
car.seatNumber = 5;
|
||||
RestAssured.given().header("Content-Type", "application/json").body(car)
|
||||
.when().post("/vehicles")
|
||||
.then().statusCode(201);
|
||||
|
||||
Moto moto = new Moto();
|
||||
moto.name = "Harley Davidson Sportster";
|
||||
moto.type = "MOTO";
|
||||
RestAssured.given().header("Content-Type", "application/json").body(moto)
|
||||
.when().post("/vehicles")
|
||||
.then().statusCode(201);
|
||||
|
||||
get("/vehicles").then().statusCode(200).body("size()", is(2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user