diff --git a/jackson-modules/jackson-conversions-3/pom.xml b/jackson-modules/jackson-conversions-3/pom.xml index ac4fbfcfb0a6..1790852e979b 100644 --- a/jackson-modules/jackson-conversions-3/pom.xml +++ b/jackson-modules/jackson-conversions-3/pom.xml @@ -42,6 +42,27 @@ json ${json.version} + + org.mongojack + mongojack + ${mongojack.version} + + + org.mongodb + mongodb-driver-sync + ${mongodb-driver.version} + + + de.undercouch + bson4jackson + ${bson4jackson.version} + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + ${flapdoodle.version} + test + @@ -57,6 +78,10 @@ 5.13.2 20240303 + 5.0.3 + 5.6.1 + 2.18.0 + 4.21.0 \ No newline at end of file diff --git a/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/BsonProductMapper.java b/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/BsonProductMapper.java new file mode 100644 index 000000000000..a2befff9fd66 --- /dev/null +++ b/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/BsonProductMapper.java @@ -0,0 +1,48 @@ +package com.baeldung.jackson.pojomapping; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.undercouch.bson4jackson.BsonFactory; +import org.bson.BsonBinaryWriter; +import org.bson.BsonDocument; +import org.bson.Document; +import org.bson.RawBsonDocument; +import org.bson.codecs.BsonDocumentCodec; +import org.bson.codecs.EncoderContext; +import org.bson.io.BasicOutputBuffer; + +import java.io.IOException; + +public class BsonProductMapper { + + private final ObjectMapper objectMapper; + + public BsonProductMapper() { + this.objectMapper = new ObjectMapper(new BsonFactory()); + } + + public byte[] toBytes(Product product) throws JsonProcessingException { + return objectMapper.writeValueAsBytes(product); + } + + public Product fromBytes(byte[] bson) throws IOException { + return objectMapper.readValue(bson, Product.class); + } + + public Document toDocument(Product product) throws IOException { + byte[] bytes = toBytes(product); + RawBsonDocument bsonDoc = new RawBsonDocument(bytes); + return Document.parse(bsonDoc.toJson()); + } + + public Product fromDocument(Document document) throws IOException { + BsonDocument bsonDoc = document.toBsonDocument(); + BasicOutputBuffer buffer = new BasicOutputBuffer(); + new BsonDocumentCodec().encode( + new BsonBinaryWriter(buffer), + bsonDoc, + EncoderContext.builder().build() + ); + return fromBytes(buffer.toByteArray()); + } +} diff --git a/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/Product.java b/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/Product.java new file mode 100644 index 000000000000..d06bb33f3875 --- /dev/null +++ b/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/Product.java @@ -0,0 +1,45 @@ +package com.baeldung.jackson.pojomapping; + +import org.mongojack.Id; +import org.mongojack.ObjectId; + +public class Product { + + @ObjectId + @Id + private String id; + private String name; + private double price; + + public Product() { + } + + public Product(String name, double price) { + this.name = name; + this.price = price; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } +} diff --git a/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/ProductService.java b/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/ProductService.java new file mode 100644 index 000000000000..c132f9bdad9f --- /dev/null +++ b/jackson-modules/jackson-conversions-3/src/main/java/com/baeldung/jackson/pojomapping/ProductService.java @@ -0,0 +1,27 @@ +package com.baeldung.jackson.pojomapping; + +import com.mongodb.client.MongoDatabase; +import org.bson.UuidRepresentation; +import org.mongojack.JacksonMongoCollection; + +public class ProductService { + + private final JacksonMongoCollection collection; + + public ProductService(MongoDatabase database) { + this.collection = JacksonMongoCollection.builder() + .build(database, "products", Product.class, UuidRepresentation.STANDARD); + } + + public void save(Product product) { + collection.insertOne(product); + } + + public Product findById(String id) { + return collection.findOneById(id); + } + + public long count() { + return collection.countDocuments(); + } +} diff --git a/jackson-modules/jackson-conversions-3/src/test/java/com/baeldung/jackson/pojomapping/BsonProductMapperUnitTest.java b/jackson-modules/jackson-conversions-3/src/test/java/com/baeldung/jackson/pojomapping/BsonProductMapperUnitTest.java new file mode 100644 index 000000000000..b6c9774abe07 --- /dev/null +++ b/jackson-modules/jackson-conversions-3/src/test/java/com/baeldung/jackson/pojomapping/BsonProductMapperUnitTest.java @@ -0,0 +1,82 @@ +package com.baeldung.jackson.pojomapping; + +import org.bson.Document; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +class BsonProductMapperUnitTest { + + private BsonProductMapper mapper; + private Product product; + + @BeforeEach + void setUp() { + mapper = new BsonProductMapper(); + product = new Product("Test Product", 29.99); + } + + @Test + void whenSerializingProduct_thenReturnsByteArray() throws IOException { + byte[] bytes = mapper.toBytes(product); + + assertNotNull(bytes); + assertTrue(bytes.length > 0); + } + + @Test + void givenSerializedProduct_whenDeserializing_thenReturnsProduct() throws IOException { + byte[] bytes = mapper.toBytes(product); + + Product deserializedProduct = mapper.fromBytes(bytes); + + assertEquals(product.getName(), deserializedProduct.getName()); + assertEquals(product.getPrice(), deserializedProduct.getPrice(), 0.01); + } + + @Test + void whenConvertingProductToDocument_thenReturnsValidDocument() throws IOException { + Document document = mapper.toDocument(product); + + assertNotNull(document); + assertEquals(product.getName(), document.getString("name")); + assertEquals(product.getPrice(), document.getDouble("price"), 0.01); + } + + @Test + void whenRoundTrippingProduct_thenDataIsPreserved() throws IOException { + Document document = mapper.toDocument(product); + Product roundTrippedProduct = mapper.fromDocument(document); + + assertEquals(product.getName(), roundTrippedProduct.getName()); + assertEquals(product.getPrice(), roundTrippedProduct.getPrice(), 0.01); + } + + @Test + void givenDocument_whenConvertingToProduct_thenReturnsProduct() throws IOException { + Document document = new Document() + .append("name", "Document Product") + .append("price", 49.99); + + Product convertedProduct = mapper.fromDocument(document); + + assertEquals("Document Product", convertedProduct.getName()); + assertEquals(49.99, convertedProduct.getPrice(), 0.01); + } + + @Test + void givenProductWithNullFields_whenSerializing_thenHandlesGracefully() throws IOException { + + Product productWithNulls = new Product(); + productWithNulls.setPrice(10.0); + + byte[] bytes = mapper.toBytes(productWithNulls); + Product deserializedProduct = mapper.fromBytes(bytes); + + assertNull(deserializedProduct.getName()); + assertEquals(10.0, deserializedProduct.getPrice(), 0.01); + } +} diff --git a/jackson-modules/jackson-conversions-3/src/test/java/com/baeldung/jackson/pojomapping/ProductServiceLiveTest.java b/jackson-modules/jackson-conversions-3/src/test/java/com/baeldung/jackson/pojomapping/ProductServiceLiveTest.java new file mode 100644 index 000000000000..c478e7caf8fc --- /dev/null +++ b/jackson-modules/jackson-conversions-3/src/test/java/com/baeldung/jackson/pojomapping/ProductServiceLiveTest.java @@ -0,0 +1,62 @@ +package com.baeldung.jackson.pojomapping; + +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import de.flapdoodle.embed.mongo.distribution.Version; +import de.flapdoodle.embed.mongo.transitions.Mongod; +import de.flapdoodle.embed.mongo.transitions.RunningMongodProcess; +import de.flapdoodle.reverse.TransitionWalker; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ProductServiceLiveTest { + + private TransitionWalker.ReachedState mongodbProcess; + private MongoClient mongoClient; + private ProductService productService; + + @BeforeEach + void setUp() { + mongodbProcess = Mongod.instance().start(Version.Main.V5_0); + var serverAddress = mongodbProcess.current().getServerAddress(); + + mongoClient = MongoClients.create(String.format("mongodb://%s:%d", + serverAddress.getHost(), serverAddress.getPort())); + productService = new ProductService(mongoClient.getDatabase("testdb")); + } + + @AfterEach + void tearDown() { + if (mongoClient != null) { + mongoClient.close(); + } + if (mongodbProcess != null) { + mongodbProcess.close(); + } + } + + @Test + void whenSavingProduct_thenProductIsPersisted() { + Product product = new Product("Laptop", 999.99); + + productService.save(product); + + assertNotNull(product.getId()); + assertEquals(1, productService.count()); + } + + @Test + void whenSavingProductWithAllFields_thenAllFieldsArePersisted() { + Product product = new Product("Full Product", 199.99); + + productService.save(product); + Product foundProduct = productService.findById(product.getId()); + + assertNotNull(foundProduct.getId()); + assertEquals("Full Product", foundProduct.getName()); + assertEquals(199.99, foundProduct.getPrice(), 0.01); + } +}