From f415210e6568f384d6a6627867fe525e959d3102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Tue, 17 Mar 2020 15:25:20 +0100 Subject: [PATCH 1/3] Hibernate with Panache deleteById() --- .../main/asciidoc/hibernate-orm-panache.adoc | 6 ++ .../orm/panache/PanacheEntityBase.java | 11 +++ .../orm/panache/PanacheRepositoryBase.java | 11 +++ .../orm/panache/runtime/JpaOperations.java | 11 +++ .../it/panache/ObjectWithCompositeId.java | 49 ++++++++++ .../it/panache/ObjectWithEmbeddableId.java | 47 ++++++++++ .../io/quarkus/it/panache/TestEndpoint.java | 92 +++++++++++++++---- .../it/panache/PanacheFunctionalityTest.java | 8 ++ 8 files changed, 219 insertions(+), 16 deletions(-) create mode 100755 integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithCompositeId.java create mode 100755 integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithEmbeddableId.java diff --git a/docs/src/main/asciidoc/hibernate-orm-panache.adoc b/docs/src/main/asciidoc/hibernate-orm-panache.adoc index 358f4226b4780..d08ae896e103a 100644 --- a/docs/src/main/asciidoc/hibernate-orm-panache.adoc +++ b/docs/src/main/asciidoc/hibernate-orm-panache.adoc @@ -195,6 +195,9 @@ Person.delete("status", Status.Alive); // delete all persons Person.deleteAll(); +// delete by id +boolean deleted = Person.deleteById(personId); + // update all living persons Person.update("name = 'Moral' where status = ?1", Status.Alive); @@ -379,6 +382,9 @@ personRepository.delete("status", Status.Alive); // delete all persons personRepository.deleteAll(); +// delete by id +boolean deleted = personRepository.deleteById(personId); + // update all living persons personRepository.update("name = 'Moral' where status = ?1", Status.Alive); diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java index 362efb9aaa921..3372cacc4f7fc 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java @@ -638,6 +638,17 @@ public static long deleteAll() { throw JpaOperations.implementationInjectionMissing(); } + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity is not delete (not found). + */ + @GenerateBridge + public static boolean deleteById(Object id) { + throw JpaOperations.implementationInjectionMissing(); + } + /** * Delete all entities of this type matching the given query, with optional indexed parameters. * diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java index 2b474e29ac1e8..4f63444aa8867 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java @@ -634,6 +634,17 @@ public default long deleteAll() { throw JpaOperations.implementationInjectionMissing(); } + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity is not deleted (not found). + */ + @GenerateBridge + public default boolean deleteById(Id id) { + throw JpaOperations.implementationInjectionMissing(); + } + /** * Delete all entities of this type matching the given query, with optional indexed parameters. * diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java index 28d2aa9216616..7f14730372067 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java @@ -393,6 +393,17 @@ public static long deleteAll(Class entityClass) { return (long) getEntityManager().createQuery("DELETE FROM " + getEntityName(entityClass)).executeUpdate(); } + public static boolean deleteById(Class entityClass, Object id) { + // Impl note : we load the entity then delete it because it's the only implementation generic enough for any model, + // and correct in all cases (composite key, graph of entities, ...). HQL cannot be directly used for these reasons. + Object entity = findById(entityClass, id); + if (entity == null) { + return false; + } + getEntityManager().remove(entity); + return true; + } + public static long delete(Class entityClass, String query, Object... params) { return bindParameters(getEntityManager().createQuery(createDeleteQuery(entityClass, query, paramCount(params))), params) .executeUpdate(); diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithCompositeId.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithCompositeId.java new file mode 100755 index 0000000000000..ded253d4994d9 --- /dev/null +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithCompositeId.java @@ -0,0 +1,49 @@ +package io.quarkus.it.panache; + +import java.io.Serializable; +import java.util.Objects; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; + +@Entity +@IdClass(ObjectWithCompositeId.ObjectKey.class) +public class ObjectWithCompositeId extends PanacheEntityBase { + @Id + public String part1; + @Id + public String part2; + public String description; + + static class ObjectKey implements Serializable { + private String part1; + private String part2; + + public ObjectKey() { + } + + public ObjectKey(String part1, String part2) { + this.part1 = part1; + this.part2 = part2; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ObjectKey objectKey = (ObjectKey) o; + return part1.equals(objectKey.part1) && + part2.equals(objectKey.part2); + } + + @Override + public int hashCode() { + return Objects.hash(part1, part2); + } + } +} diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithEmbeddableId.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithEmbeddableId.java new file mode 100755 index 0000000000000..28d3d869a3ab5 --- /dev/null +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/ObjectWithEmbeddableId.java @@ -0,0 +1,47 @@ +package io.quarkus.it.panache; + +import java.io.Serializable; +import java.util.Objects; + +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; + +@Entity +public class ObjectWithEmbeddableId extends PanacheEntityBase { + @EmbeddedId + public ObjectKey key; + public String description; + + @Embeddable + static class ObjectKey implements Serializable { + private String part1; + private String part2; + + public ObjectKey() { + } + + public ObjectKey(String part1, String part2) { + this.part1 = part1; + this.part2 = part2; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ObjectKey objectKey = (ObjectKey) o; + return part1.equals(objectKey.part1) && + part2.equals(objectKey.part2); + } + + @Override + public int hashCode() { + return Objects.hash(part1, part2); + } + } +} diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java index d560a0aa7a30f..b95d7befaa34e 100644 --- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java @@ -1,8 +1,10 @@ package io.quarkus.it.panache; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -29,6 +31,7 @@ import org.hibernate.jpa.QueryHints; import org.junit.jupiter.api.Assertions; +import io.quarkus.hibernate.orm.panache.Panache; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Page; import io.quarkus.panache.common.Parameters; @@ -214,6 +217,14 @@ public String testModel() { testUpdate(); + //delete by id + Person toRemove = new Person(); + toRemove.name = "testDeleteById"; + toRemove.uniqueName = "testDeleteByIdUnique"; + toRemove.persist(); + assertTrue(Person.deleteById(toRemove.id)); + Person.deleteById(666L); //not existing + // persistAndFlush Person person1 = new Person(); person1.name = "testFLush1"; @@ -459,8 +470,8 @@ private void testPersist(PersistTest persistTest) { person1.name = "stef1"; Person person2 = new Person(); person2.name = "stef2"; - Assertions.assertFalse(person1.isPersistent()); - Assertions.assertFalse(person2.isPersistent()); + assertFalse(person1.isPersistent()); + assertFalse(person2.isPersistent()); switch (persistTest) { case Iterable: Person.persist(Arrays.asList(person1, person2)); @@ -472,8 +483,8 @@ private void testPersist(PersistTest persistTest) { Person.persist(person1, person2); break; } - Assertions.assertTrue(person1.isPersistent()); - Assertions.assertTrue(person2.isPersistent()); + assertTrue(person1.isPersistent()); + assertTrue(person2.isPersistent()); } @Inject @@ -499,11 +510,11 @@ public String testModelDao() { } catch (NoResultException x) { } - Assertions.assertFalse(personDao.findAll().singleResultOptional().isPresent()); + assertFalse(personDao.findAll().singleResultOptional().isPresent()); Assertions.assertNull(personDao.findAll().firstResult()); - Assertions.assertFalse(personDao.findAll().firstResultOptional().isPresent()); + assertFalse(personDao.findAll().firstResultOptional().isPresent()); Person person = makeSavedPersonDao(); Assertions.assertNotNull(person.id); @@ -645,6 +656,14 @@ public String testModelDao() { testUpdateDAO(); + //delete by id + Person toRemove = new Person(); + toRemove.name = "testDeleteById"; + toRemove.uniqueName = "testDeleteByIdUnique"; + personDao.persist(toRemove); + assertTrue(personDao.deleteById(toRemove.id)); + personDao.deleteById(666L);//not existing + //flush Person person1 = new Person(); person1.name = "testFlush1"; @@ -735,8 +754,8 @@ private void testPersistDao(PersistTest persistTest) { person1.name = "stef1"; Person person2 = new Person(); person2.name = "stef2"; - Assertions.assertFalse(person1.isPersistent()); - Assertions.assertFalse(person2.isPersistent()); + assertFalse(person1.isPersistent()); + assertFalse(person2.isPersistent()); switch (persistTest) { case Iterable: personDao.persist(Arrays.asList(person1, person2)); @@ -748,8 +767,8 @@ private void testPersistDao(PersistTest persistTest) { personDao.persist(person1, person2); break; } - Assertions.assertTrue(person1.isPersistent()); - Assertions.assertTrue(person2.isPersistent()); + assertTrue(person1.isPersistent()); + assertTrue(person2.isPersistent()); } private Person makeSavedPersonDao(String suffix) { @@ -827,8 +846,8 @@ private void testPaging(PanacheQuery query) { Assertions.assertEquals("stef0", persons.get(0).name); Assertions.assertEquals("stef1", persons.get(1).name); Assertions.assertEquals("stef2", persons.get(2).name); - Assertions.assertTrue(query.hasNextPage()); - Assertions.assertFalse(query.hasPreviousPage()); + assertTrue(query.hasNextPage()); + assertFalse(query.hasPreviousPage()); persons = query.nextPage().list(); Assertions.assertEquals(1, query.page().index); @@ -837,14 +856,14 @@ private void testPaging(PanacheQuery query) { Assertions.assertEquals("stef3", persons.get(0).name); Assertions.assertEquals("stef4", persons.get(1).name); Assertions.assertEquals("stef5", persons.get(2).name); - Assertions.assertTrue(query.hasNextPage()); - Assertions.assertTrue(query.hasPreviousPage()); + assertTrue(query.hasNextPage()); + assertTrue(query.hasPreviousPage()); persons = query.nextPage().list(); Assertions.assertEquals(1, persons.size()); Assertions.assertEquals("stef6", persons.get(0).name); - Assertions.assertFalse(query.hasNextPage()); - Assertions.assertTrue(query.hasPreviousPage()); + assertFalse(query.hasNextPage()); + assertTrue(query.hasPreviousPage()); persons = query.nextPage().list(); Assertions.assertEquals(0, persons.size()); @@ -1060,6 +1079,47 @@ private void ensureFieldSanitized(String fieldName) throws Exception { assertNotNull(f.getAnnotation(XmlTransient.class)); } + @GET + @Path("composite") + @Transactional + public String testCompositeKey() { + ObjectWithCompositeId obj = new ObjectWithCompositeId(); + obj.part1 = "part1"; + obj.part2 = "part2"; + obj.description = "description"; + obj.persist(); + + ObjectWithCompositeId.ObjectKey key = new ObjectWithCompositeId.ObjectKey("part1", "part2"); + ObjectWithCompositeId result = ObjectWithCompositeId.findById(key); + assertNotNull(result); + + boolean deleted = ObjectWithCompositeId.deleteById(key); + assertTrue(deleted); + + ObjectWithCompositeId.ObjectKey notExistingKey = new ObjectWithCompositeId.ObjectKey("notexist1", "notexist2"); + deleted = ObjectWithCompositeId.deleteById(key); + assertFalse(deleted); + + ObjectWithEmbeddableId.ObjectKey embeddedKey = new ObjectWithEmbeddableId.ObjectKey("part1", "part2"); + ObjectWithEmbeddableId embeddable = new ObjectWithEmbeddableId(); + embeddable.key = embeddedKey; + embeddable.description = "description"; + embeddable.persist(); + + ObjectWithEmbeddableId embeddableResult = ObjectWithEmbeddableId.findById(embeddedKey); + assertNotNull(embeddableResult); + + deleted = ObjectWithEmbeddableId.deleteById(embeddedKey); + assertTrue(deleted); + + ObjectWithEmbeddableId.ObjectKey notExistingEmbeddedKey = new ObjectWithEmbeddableId.ObjectKey("notexist1", + "notexist2"); + deleted = ObjectWithEmbeddableId.deleteById(embeddedKey); + assertFalse(deleted); + + return "OK"; + } + @GET @Path("7721") @Transactional diff --git a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java index 5e54f0f7f9ebb..ce6e45e07665e 100644 --- a/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java +++ b/integration-tests/hibernate-orm-panache/src/test/java/io/quarkus/it/panache/PanacheFunctionalityTest.java @@ -98,6 +98,14 @@ public void jacksonDeserializationIgnoresPersistentAttribute() throws JsonProces personAsString); } + @Test + public void testCompositeKey() { + RestAssured.when() + .get("/test/composite") + .then() + .body(is("OK")); + } + @Test public void testBug7721() { RestAssured.when().get("/test/7721").then().body(is("OK")); From 0e65e4131e135093905beafd6101d126636d268a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Tue, 17 Mar 2020 15:25:13 +0100 Subject: [PATCH 2/3] MongoDB with Panache deleteById() --- docs/src/main/asciidoc/mongodb-panache.adoc | 9 +++++++++ .../hibernate/orm/panache/PanacheEntityBase.java | 2 +- .../hibernate/orm/panache/PanacheRepositoryBase.java | 2 +- .../mongodb/panache/PanacheMongoEntityBase.java | 11 +++++++++++ .../mongodb/panache/PanacheMongoRepositoryBase.java | 11 +++++++++++ .../reactive/ReactivePanacheMongoEntityBase.java | 12 ++++++++++++ .../reactive/ReactivePanacheMongoRepositoryBase.java | 12 ++++++++++++ .../reactive/runtime/ReactiveMongoOperations.java | 6 ++++++ .../mongodb/panache/runtime/MongoOperations.java | 8 ++++++++ .../it/mongodb/panache/book/BookEntityResource.java | 6 ++++-- .../mongodb/panache/book/BookRepositoryResource.java | 6 ++++-- .../reactive/book/ReactiveBookEntityResource.java | 8 +++++++- .../book/ReactiveBookRepositoryResource.java | 8 +++++++- 13 files changed, 93 insertions(+), 8 deletions(-) diff --git a/docs/src/main/asciidoc/mongodb-panache.adoc b/docs/src/main/asciidoc/mongodb-panache.adoc index 4f0a3a7b8416c..39c59b5dea177 100644 --- a/docs/src/main/asciidoc/mongodb-panache.adoc +++ b/docs/src/main/asciidoc/mongodb-panache.adoc @@ -217,6 +217,9 @@ Person.delete("status", Status.Alive); // delete all persons Person.deleteAll(); + +// delete by id +boolean deleted = Person.deleteById(personId); ---- All `list` methods have equivalent `stream` versions. @@ -378,6 +381,9 @@ personRepository.delete("status", Status.Alive); // delete all persons personRepository.deleteAll(); + +// delete by id +boolean deleted = personRepository.deleteById(personId); ---- All `list` methods have equivalent `stream` versions. @@ -771,6 +777,9 @@ Uni deleteCount = ReactivePerson.delete("status", Status.Alive); // delete all persons deleteCount = ReactivePerson.deleteAll(); + +// delete by id +Uni deleted = ReactivePerson.deleteById(personId); ---- TIP: If you use MongoDB with Panache in conjunction with RESTEasy, you can directly return a reactive type inside your JAX-RS resource endpoint as long as you include the `quarkus-resteasy-mutiny` extension. diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java index 3372cacc4f7fc..74ed7d23ee51d 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java @@ -642,7 +642,7 @@ public static long deleteAll() { * Delete an entity of this type by ID. * * @param id the ID of the entity to delete. - * @return false if the entity is not delete (not found). + * @return false if the entity was not deleted (not found). */ @GenerateBridge public static boolean deleteById(Object id) { diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java index 4f63444aa8867..6b26a4c17d4ce 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java @@ -638,7 +638,7 @@ public default long deleteAll() { * Delete an entity of this type by ID. * * @param id the ID of the entity to delete. - * @return false if the entity is not deleted (not found). + * @return false if the entity was not deleted (not found). */ @GenerateBridge public default boolean deleteById(Id id) { diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java index f924fb22f09f4..be4d2a1169ed9 100755 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java @@ -697,6 +697,17 @@ public static long deleteAll() { throw MongoOperations.implementationInjectionMissing(); } + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity was not deleted (not found). + */ + @GenerateBridge + public static boolean deleteById(Object id) { + throw MongoOperations.implementationInjectionMissing(); + } + /** * Delete all entities of this type matching the given query, with optional indexed parameters. * diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java index 532328334129c..e2ecf4351c047 100755 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java @@ -702,6 +702,17 @@ public default long deleteAll() { throw MongoOperations.implementationInjectionMissing(); } + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity was not deleted (not found). + */ + @GenerateBridge + public default boolean deleteById(Id id) { + throw MongoOperations.implementationInjectionMissing(); + } + /** * Delete all entities of this type matching the given query, with optional indexed parameters. * diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoEntityBase.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoEntityBase.java index 9bc3f8e811b86..4e27080089835 100644 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoEntityBase.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoEntityBase.java @@ -8,6 +8,7 @@ import org.bson.Document; import io.quarkus.mongodb.panache.reactive.runtime.ReactiveMongoOperations; +import io.quarkus.mongodb.panache.runtime.MongoOperations; import io.quarkus.mongodb.reactive.ReactiveMongoCollection; import io.quarkus.mongodb.reactive.ReactiveMongoDatabase; import io.quarkus.panache.common.Parameters; @@ -707,6 +708,17 @@ public static Uni deleteAll() { throw ReactiveMongoOperations.implementationInjectionMissing(); } + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity was not deleted (not found). + */ + @GenerateBridge + public static Uni deleteById(Object id) { + throw MongoOperations.implementationInjectionMissing(); + } + /** * Delete all entities of this type matching the given query, with optional indexed parameters. * diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoRepositoryBase.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoRepositoryBase.java index 00555892454ad..4703fc98b28cf 100644 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoRepositoryBase.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheMongoRepositoryBase.java @@ -8,6 +8,7 @@ import org.bson.Document; import io.quarkus.mongodb.panache.reactive.runtime.ReactiveMongoOperations; +import io.quarkus.mongodb.panache.runtime.MongoOperations; import io.quarkus.mongodb.reactive.ReactiveMongoCollection; import io.quarkus.mongodb.reactive.ReactiveMongoDatabase; import io.quarkus.panache.common.Parameters; @@ -703,6 +704,17 @@ public default Uni deleteAll() { throw ReactiveMongoOperations.implementationInjectionMissing(); } + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity was not deleted (not found). + */ + @GenerateBridge + public default Uni deleteById(Id id) { + throw MongoOperations.implementationInjectionMissing(); + } + /** * Delete all entities of this type matching the given query, with optional indexed parameters. * diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java index 180e000259077..2583ba98cba26 100644 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java @@ -551,6 +551,12 @@ public static Uni deleteAll(Class entityClass) { return collection.deleteMany(new Document()).map(deleteResult -> deleteResult.getDeletedCount()); } + public static Uni deleteById(Class entityClass, Object id) { + ReactiveMongoCollection collection = mongoCollection(entityClass); + Document query = new Document().append(ID, id); + return collection.deleteOne(query).map(results -> results.getDeletedCount() == 1); + } + public static Uni delete(Class entityClass, String query, Object... params) { String bindQuery = bindQuery(entityClass, query, params); Document docQuery = Document.parse(bindQuery); diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java index ec75118bc54d0..609500b076fe0 100644 --- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java +++ b/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/runtime/MongoOperations.java @@ -25,6 +25,7 @@ import com.mongodb.client.model.ReplaceOptions; import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.WriteModel; +import com.mongodb.client.result.DeleteResult; import io.quarkus.arc.Arc; import io.quarkus.mongodb.panache.MongoEntity; @@ -522,6 +523,13 @@ public static long deleteAll(Class entityClass) { return collection.deleteMany(new Document()).getDeletedCount(); } + public static boolean deleteById(Class entityClass, Object id) { + MongoCollection collection = mongoCollection(entityClass); + Document query = new Document().append(ID, id); + DeleteResult results = collection.deleteOne(query); + return results.getDeletedCount() == 1; + } + public static long delete(Class entityClass, String query, Object... params) { String bindQuery = bindQuery(entityClass, query, params); Document docQuery = Document.parse(bindQuery); diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookEntityResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookEntityResource.java index 8cb8c21d1ddab..f8f018e81df14 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookEntityResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookEntityResource.java @@ -59,8 +59,10 @@ public Response upsertBook(BookEntity book) { @DELETE @Path("/{id}") public void deleteBook(@PathParam("id") String id) { - BookEntity theBook = BookEntity.findById(new ObjectId(id)); - theBook.delete(); + boolean deleted = BookEntity.deleteById(new ObjectId(id)); + if (!deleted) { + throw new NotFoundException(); + } } @GET diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookRepositoryResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookRepositoryResource.java index 51f6852b8790f..5de29dc6f49f4 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookRepositoryResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/book/BookRepositoryResource.java @@ -62,8 +62,10 @@ public Response upsertBook(Book book) { @DELETE @Path("/{id}") public void deleteBook(@PathParam("id") String id) { - Book theBook = bookRepository.findById(new ObjectId(id)); - bookRepository.delete(theBook); + boolean deleted = bookRepository.deleteById(new ObjectId(id)); + if (!deleted) { + throw new NotFoundException(); + } } @GET diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookEntityResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookEntityResource.java index 4fb0a981ddf2c..54d11aee5cf3e 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookEntityResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookEntityResource.java @@ -73,7 +73,13 @@ public Uni upsertBook(ReactiveBookEntity book) { @DELETE @Path("/{id}") public Uni deleteBook(@PathParam("id") String id) { - return ReactiveBookEntity.findById(new ObjectId(id)).flatMap(book -> book.delete()); + return ReactiveBookEntity.deleteById(new ObjectId(id)) + .map(d -> { + if (d) { + return null; + } + throw new NotFoundException(); + }); } @GET diff --git a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookRepositoryResource.java b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookRepositoryResource.java index 0b340e726c161..543dab581b105 100644 --- a/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookRepositoryResource.java +++ b/integration-tests/mongodb-panache/src/main/java/io/quarkus/it/mongodb/panache/reactive/book/ReactiveBookRepositoryResource.java @@ -77,7 +77,13 @@ public Uni upsertBook(Book book) { @DELETE @Path("/{id}") public Uni deleteBook(@PathParam("id") String id) { - return reactiveBookRepository.findById(new ObjectId(id)).flatMap(book -> reactiveBookRepository.delete(book)); + return reactiveBookRepository.deleteById(new ObjectId(id)) + .map(d -> { + if (d) { + return null; + } + throw new NotFoundException(); + }); } @GET From 65ca54c2810db1ac72100db4a235554dc4faa6f3 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 3 Mar 2020 14:05:45 +0200 Subject: [PATCH 3/3] Simplify Spring Data JPA deleteById implementation Use the newly added JpaOperations#deleteById --- .../generate/StockMethodsAdder.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java index de87172ad5a6c..f7e3a7ece6e63 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java @@ -707,35 +707,32 @@ private void generateDeleteById(ClassCreator classCreator, FieldDescriptor entit ResultHandle entityClass = deleteById.readInstanceField(entityClassFieldDescriptor, deleteById.getThis()); - ResultHandle entity = deleteById.invokeStaticMethod( - MethodDescriptor.ofMethod(JpaOperations.class, "findById", Object.class, Class.class, + ResultHandle deleted = deleteById.invokeStaticMethod( + MethodDescriptor.ofMethod(JpaOperations.class, "deleteById", boolean.class, Class.class, Object.class), entityClass, id); - BranchResult entityNullBranch = deleteById.ifNull(entity); - BytecodeCreator entityNull = entityNullBranch.trueBranch(); + BranchResult deletedBranch = deleteById.ifNonZero(deleted); + BytecodeCreator deletedFalse = deletedBranch.falseBranch(); - ResultHandle idToString = entityNull.invokeVirtualMethod( + ResultHandle idToString = deletedFalse.invokeVirtualMethod( ofMethod(Object.class, "toString", String.class), id); - ResultHandle formatArgsArray = entityNull.newArray(Object.class, 1); - entityNull.writeArrayValue(formatArgsArray, entityNull.load(0), idToString); + ResultHandle formatArgsArray = deletedFalse.newArray(Object.class, 1); + deletedFalse.writeArrayValue(formatArgsArray, deletedFalse.load(0), idToString); - ResultHandle messageFormat = entityNull.load("No entity " + entityTypeStr + " with id %s exists"); - ResultHandle message = entityNull.invokeStaticMethod( + ResultHandle messageFormat = deletedFalse.load("No entity " + entityTypeStr + " with id %s exists"); + ResultHandle message = deletedFalse.invokeStaticMethod( MethodDescriptor.ofMethod(String.class, "format", String.class, String.class, Object[].class), messageFormat, formatArgsArray); - ResultHandle exception = entityNull.newInstance( + ResultHandle exception = deletedFalse.newInstance( MethodDescriptor.ofConstructor(IllegalArgumentException.class, String.class), message); - entityNull.throwException(exception); + deletedFalse.throwException(exception); + deletedFalse.breakScope(); - BytecodeCreator entityNotNull = entityNullBranch.falseBranch(); - entityNotNull.invokeStaticMethod( - MethodDescriptor.ofMethod(JpaOperations.class, "delete", void.class, Object.class), - entity); - entityNotNull.returnValue(null); + deleteById.returnValue(null); } try (MethodCreator bridgeDeleteById = classCreator.getMethodCreator(bridgeDeleteByIdDescriptor)) { MethodDescriptor deleteById = MethodDescriptor.ofMethod(generatedClassName, "deleteById",