ããã¯ããªã«ãããããŠæžãããã®ïŒ
Quarkusã«ãHibernate Reactive with Panacheãšãããã®ã«é¢ããããã¥ã¡ã³ããããã®ãåã ããæ°ã«ãªã£ãŠããã®ã§ã
Quarkus - Simplified Hibernate Reactive with Panache
ä»åã1床詊ããŠã¿ãããšã«ããŸããã
çµæã ãèšããšããŸãæéãçµã£ãŠããèŠãŠã¿ããããªããšããæ°åã«ã¯ãªããŸãããããšãããããæ
å ±ã¯ããããèŠãŠã¿ãã®ã§
ãã®ããããæžããŠããããšæããŸãã
Hibernate Reactive
ãŸãã¯ãHibernate Reactiveã«ã€ããŠã
The reactive API for Hibernate ORM. - Hibernate Reactive
Hibernate Reactiveã¯ãååã®ãšããHibernate ORMã®ãªã¢ã¯ãã£ãçãšãã£ãæãã®ãã®ã§ãã
ããŒã¿ããŒã¹ã®ãã©ã€ããŒãšããŠã¯ãJDBCã§ã¯ãªãVert.xã®ãªã¢ã¯ãã£ãããŒã¿ããŒã¹ã¯ã©ã€ã¢ã³ãã䜿çšããŸãã
Hibernate Reactiveã®ããã¥ã¡ã³ãã¯ãã¡ãã
Hibernate Reactive 1.1.2.Final Reference Documentation
çŸæç¹ã§ããŒãžã§ã³1.0ã«å°éããŠããŠãåºæ¬çãªæ
å ±ã¯Hibernate ORMãJPAã®ããã¥ã¡ã³ããåç
§ããããšããšãªã£ãŠããŸãã
JPAã®ãšã³ãã£ãã£çšã®ã¢ãããŒã·ã§ã³ã䜿ããããããã®ã§ãHibernate ORMãšåçã®ããšãã§ããã®ããšæãããããã§ããªãã£ããããŸãã
ããšãã°ãçŸæç¹ã§Hibernate Reactiveã¯JPAã®ãã©ã³ã¶ã¯ã·ã§ã³ç®¡çãšã®çµ±åããè€æ°ã®ããŒã¿ãœãŒã¹ãæ±ãããšã¯ã§ããŸããã
JTAã«ã察å¿ããŠããªãã®ã§ã@Transactionalã¢ãããŒã·ã§ã³ã«ãããã©ã³ã¶ã¯ã·ã§ã³ç®¡çã¯ã§ããªããšããããšã§ããã
However, notice that the transaction is a resource local transaction only, delegated to the underlying Vert.x database client, and does not span multiple datasources, nor integrate with JPA container-managed transactions.
Hibernate Reactive does not currently support distributed (XA) transactions.
Hibernate Reactive Reference Documentation / Transactions
Quarkus Hibernate Reactive with Panache Extension
次ã«ãQuarkusã®Hibernate Reactive with Panache Extensionã«ã€ããŠã
Quarkus - Simplified Hibernate Reactive with Panache
Hibernate ORM Extensionã«ã¯ãHibernate ORMãšPanacheããããããã¥ã¡ã³ããããã®ã§ãããHibernate Reactiveã®æ¹ã«ã¯
Panacheãšåããããã®ãããããŸããã
Quarkus - Using Hibernate ORM and JPA
Quarkus - Simplified Hibernate ORM with Panache
Panacheãšããã®ã¯ãQuarkusã§Hibernateãç°¡åã«äœ¿ããããã«ããããã®Extensionã§ãã
Quarkusã§Hibernate ORMã䜿ãæç¹ã§ãpersistence.xmlãæžããªããŠãè¯ããªã©ã䜿ããããã¯ãªã£ãŠããã®ã§ããã
Panacheã䜿ãããšã§ã以äžã®ããšãã§ããããã«ãªããŸãã
- JPAã®ãšã³ãã£ãã£ã¯ã©ã¹ã
PanacheEntityã¯ã©ã¹ïŒãŸãã¯PanacheEntityBaseã¯ã©ã¹ïŒã®ãµãã¯ã©ã¹ãšããããšã«ãããActive Record Patternã®å©çš PanacheRepositoryã€ã³ã¿ãŒãã§ãŒã¹ïŒãŸãã¯PanacheRepositoryBaseã€ã³ã¿ãŒãã§ãŒã¹ïŒãå®è£ ãããªããžããªã¯ã©ã¹ãäœæããããšã«ãããRepository Patternã®å©çš- ã¢ãã¯ã®ãµããŒã
ãã¿ãŒã³ã«ã€ããŠã¯ãã©ã¡ãã®æ¹æ³ãéžãã§ã䟿å©ãªã¯ãšãªçšã®ã¡ãœããã远å ãããŠããããJPQLå šäœãæžããªããŠãç°¡æã«JPQLçžåœã®ãã®ããã æžããããããŒãžã³ã°ãã§ããããšHibernate ORMãç°¡åã«äœ¿ããããã«ãªããŸãã
Hibernate Reactive with Panacheãåºæ¬çã«ã¯åã話ãªã®ã§ãããActive RecordãšRepositoryã®åãã¿ãŒã³ãšã¢ãã¯ã«å ããŠã以äžã®ããšã
ã§ããããã«ãªããŸãã
@ReactiveTransactionalã¢ãããŒã·ã§ã³ã䜿ã£ã宣èšçãã©ã³ã¶ã¯ã·ã§ã³ã®ãµããŒã
Simplified Hibernate Reactive with Panache / Transactions
ãã ãã¢ãã¯ã«ã€ããŠã¯Repository Patternã®ã¿ã®ãµããŒãã«ãªããŸãã
JTAã®@Transactionalã§ã¯ãHibernate Reactiveã®ãã©ã³ã¶ã¯ã·ã§ã³ç®¡çã¯ã§ããŸãããããã¥ã¡ã³ããèªãã§ããŠãããã¯ã©ãããŠã ããïŒãš
æã£ãŠããã®ã§ãããåè¿°ã®ãšããããããHibernate ReactiveãJTAã®ãã©ã³ã¶ã¯ã·ã§ã³ã«å¯Ÿå¿ããŠããªãããã§ãã
Hibernate Reactiveã§ã®ãã©ã³ã¶ã¯ã·ã§ã³ç®¡çã¯ã以äžã®ã€ã¡ãŒãžã«ãªããŸãã
session.withTransaction( tx -> session.persist(book) )
@ReactiveTransactionalã¢ãããŒã·ã§ã³ãä»äžãããšããããã€ã³ã¿ãŒã»ãã¿ãŒã§è¡ãããšã«ãªããŸãã
泚æç¹ãšããŠã@ReactiveTransactionalã¢ãããŒã·ã§ã³ãä»äžããã¡ãœããã¯Uniãè¿ãããšã«ãªãã®ã§ããããã©ã³ã¶ã¯ã·ã§ã³æäœã¯
ãã®Uniå
ã§äœ¿çšãããªãã¬ãŒã¿ãŒå
ã«ããªããã°ãªããªããšããããšã§ããã
quarkus/ReactiveTransactionalInterceptorBase.java at 2.6.3.Final · quarkusio/quarkus · GitHub
ã¡ãœããã®æ»ãå€ãšãªãUni以å€ã®ãšããã«ããæŽæ°åŠçã¯ããã©ã³ã¶ã¯ã·ã§ã³ã®ç¯å²å€ã«ãªããŸãã
ãšããã§ãã®@ReactiveTransactionalã¢ãããŒã·ã§ã³ãHibernate Reactive Extensionã§ã¯ãªãHibernate Reactive with Panacheã®æ¹ã«ããã®ã¯
ã©ãããŠãªãã§ããããã
ããã¥ã¡ã³ãã«èšèŒã¯ãªããã®ã®ãHibernate Reactive Extensionãååšã¯ããããã§ããã

ããèŠããšãäž¡æ¹ãšãPreviewã§ããâŠcode.quarkus.ioãèŠãŠæ°ã¥ããŸããâŠã
ãã¹ãã§ã®ãã©ã³ã¶ã¯ã·ã§ã³ã«é¢ããŠã¯ãJTAã§ããã°@TestTransactionãšããã¢ãããŒã·ã§ã³ãããããã§ããã
Hibernate Reactive with Panacheã«ã¯å¯Ÿå¿ãããã®ãããã¥ã¡ã³ãã«ã¯ãªãããã§ãã
ãã®ããããããã£ãœãããªããšæããŸãããããã¥ã¡ã³ãã«ã¯æžãããŠããªãã®ã§ãã£ããã¡ã¢ã®ã¿ã«ããŸãã
https://github.com/quarkusio/quarkus/tree/2.6.3.Final/test-framework/junit5-vertx
ãããŠããã²ãšã€ãçŸæç¹ã§ãHibernate Reactiveã¯FlywayâŠãšãããJDBCã䜿ãExtensionãšå ±åã§ããŸããã
ããšãã°ã以äžã®ããã«Flywayã远å ããŠQuarkusãèµ·åããããšãããš
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jdbc-mysql</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-flyway</artifactId> </dependency>
èµ·åã«å€±æããŸãã
Caused by: java.lang.RuntimeException: javax.persistence.PersistenceException: Unable to build EntityManagerFactory
at io.quarkus.hibernate.orm.runtime.JPAConfig.startAll(JPAConfig.java:72)
at io.quarkus.hibernate.orm.runtime.JPAConfig_Subclass.startAll$$superforward1(Unknown Source)
at io.quarkus.hibernate.orm.runtime.JPAConfig_Subclass$$function$$5.apply(Unknown Source)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:51)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
at io.quarkus.hibernate.orm.runtime.JPAConfig_Subclass.startAll(Unknown Source)
at io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder.startAllPersistenceUnits(HibernateOrmRecorder.java:97)
at io.quarkus.deployment.steps.HibernateOrmProcessor$startPersistenceUnits1868654632.deploy_0(Unknown Source)
at io.quarkus.deployment.steps.HibernateOrmProcessor$startPersistenceUnits1868654632.deploy(Unknown Source)
... 13 more
Caused by: javax.persistence.PersistenceException: Unable to build EntityManagerFactory
at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.createEntityManagerFactory(FastBootHibernateReactivePersistenceProvider.java:93)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:80)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at io.quarkus.hibernate.orm.runtime.JPAConfig$LazyPersistenceUnit.get(JPAConfig.java:149)
at io.quarkus.hibernate.orm.runtime.JPAConfig$1.run(JPAConfig.java:58)
... 1 more
Caused by: java.lang.IllegalStateException: Booting an Hibernate Reactive serviceregistry on a non-reactive RecordedState!
at io.quarkus.hibernate.reactive.runtime.boot.registry.PreconfiguredReactiveServiceRegistryBuilder.checkIsReactive(PreconfiguredReactiveServiceRegistryBuilder.java:77)
at io.quarkus.hibernate.reactive.runtime.boot.registry.PreconfiguredReactiveServiceRegistryBuilder.<init>(PreconfiguredReactiveServiceRegistryBuilder.java:67)
at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.rewireMetadataAndExtractServiceRegistry(FastBootHibernateReactivePersistenceProvider.java:177)
at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.getEntityManagerFactoryBuilderOrNull(FastBootHibernateReactivePersistenceProvider.java:156)
at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.createEntityManagerFactory(FastBootHibernateReactivePersistenceProvider.java:82)
... 5 more
ããã¯ãJDBCã䜿ã£ãããŒã¿ãœãŒã¹ãååšãããšHibernate Reactiveãæ€åºããŠããŸãããã®ããã§ãã
å人çã«ã¯ããããããã®æ
å ±ããŸãšãããããä»åã¯ããããããªããšæã£ãããããã®ã§ãããããããå
ã¯ãããŸãèŠãæå³ã¯
ãããŸããã
äžå¿ãã®åŸã«ã詊ããããšãå°ãæžããŠãããŸãããããŸãæéãçµã£ãŠHibernate Reactiveãé²åãããããŸãèŠãŠã¿ããããªãšæããŸãã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ java --version openjdk 17.0.1 2021-10-19 OpenJDK Runtime Environment (build 17.0.1+12-Ubuntu-120.04) OpenJDK 64-Bit Server VM (build 17.0.1+12-Ubuntu-120.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.4 (9b656c72d54e5bacbed989b64718c159fe39b537) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.1, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-96-generic", arch: "amd64", family: "unix"
ããŒã¿ããŒã¹ã«ã¯MySQLã䜿çšãã172.17.0.2ã§åäœããŠãããã®ãšããŸãã
$ mysql --version mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)
ã¢ããªã±ãŒã·ã§ã³ãäœæãã
ãŸãã¯ãQuarkusãããžã§ã¯ããäœæããŸãã
$ mvn io.quarkus.platform:quarkus-maven-plugin:2.6.3.Final:create \
-DprojectGroupId=org.littlewings \
-DprojectArtifactId=hibernate-reactive \
-DprojectVersion=0.0.1-SNAPSHOT \
-Dextensions="resteasy-reactive,hibernate-reactive-panache,reactive-mysql-client"
éžæãããExtensionã
[INFO] Looking for the newly published extensions in registry.quarkus.io [INFO] ----------- [INFO] selected extensions: - io.quarkus:quarkus-resteasy-reactive - io.quarkus:quarkus-reactive-mysql-client - io.quarkus:quarkus-hibernate-reactive-panache [INFO] applying codestarts... [INFO] ð java ðš maven ðŠ quarkus ð config-properties ð§ dockerfiles ð§ maven-wrapper ð resteasy-reactive-codestart
ãããžã§ã¯ãå ã«ç§»åããŸãã
$ cd hibernate-reactive
MavenäŸåé¢ä¿ã¯ããã®ãããªæãã«ã
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-reactive-mysql-client</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-reactive-panache</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency> </dependencies>
quarkus-hibernate-reactive-panacheãšquarkus-reactive-mysql-clientã®2ã€ããä»åå°ãªããšãå¿
èŠãªãã®ã§ãã
quarkus-resteasy-reactiveãããŸãããä»åã¯äœ¿ãã®ããããŸããã
èªåçæããããœãŒã¹ã³ãŒãã¯åé€ã
$ rm src/main/java/org/littlewings/* src/test/java/org/littlewings/*
ã¢ã¯ã»ã¹ããããŒãã«ã®ãé¡ã¯ãæžç±ãšããŸãã
create table book ( isbn varchar(14), title varchar(255), price int, primary key(isbn) );
JPAã®ãšã³ãã£ãã£ã¯ã©ã¹ããã¡ãã¯ãPanacheã«é¢ããã¯ã©ã¹ã䜿ããªãããã«ããŸããã
src/main/java/org/littlewings/quarkus/hibernate/reactive/Book.java
package org.littlewings.quarkus.hibernate.reactive; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "book") public class Book { @Id String isbn; @Column String title; @Column Integer price; public static Book create(String isbn, String title, Integer price) { Book book = new Book(); book.setIsbn(isbn); book.setTitle(title); book.setPrice(price); return book; } // getterïŒsetterã¯çç¥ã }
Panacheã¯ãRepository PatternãšããŠäœ¿ãããšã«ããŸãã
src/main/java/org/littlewings/quarkus/hibernate/reactive/BookRepository.java
package org.littlewings.quarkus.hibernate.reactive; import java.util.List; import javax.enterprise.context.ApplicationScoped; import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.panache.common.Parameters; import io.smallrye.mutiny.Uni; @ApplicationScoped public class BookRepository implements PanacheRepositoryBase<Book, String> { public Uni<Book> findByIsbn(String isbn) { return findById(isbn); } public Uni<List<Book>> findByPrice(Integer price) { return list( "price > :price order by price desc", Parameters.with("price", price) ); } }
ããã¥ã¡ã³ãã«ãããšãRepository Patternã䜿ãå Žåã¯PanacheRepositoryã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããã®ãåºæ¬ã®ããã§ããã
PanacheRepositoryã€ã³ã¿ãŒãã§ãŒã¹ã䜿çšããå Žåã¯ãšã³ãã£ãã£ã¯ã©ã¹ã®äž»ããŒïŒ@IdïŒãLongã§ããããšãåæã«ãªããŸãã
Simplified Hibernate Reactive with Panache / Solution 2: using the repository pattern
äž»ããŒãä»ã®åã§æ±ãå Žåã¯ãPanacheRepositoryBaseã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããã¯ã©ã¹ãäœæããŸãã
Simplified Hibernate Reactive with Panache / / Custom IDs
ä»åã¯ããã¡ãã®ãã¿ãŒã³ã䜿çšããŸããããã®èãæ¹ã¯ãActive Record Patternã䜿ã£ãŠãåæ§ã§ãã
ãããŠãPanacheãæäŸããã€ã³ã¿ãŒãã§ãŒã¹ïŒActive Record Patternã®å Žåã¯ã¯ã©ã¹ïŒã䜿çšããããšã§ã䟿å©ãªã¡ãœããã䜿ããããã«
ãªããŸãã
Simplified Hibernate Reactive with Panache / Most useful operations
ãããã®å®çŸ©ããPanacheRepositoryBaseã€ã³ã¿ãŒãã§ãŒã¹ãæäŸããããã©ã«ãå®è£
ãå©çšããŠããŸãã
public Uni<Book> findByIsbn(String isbn) { return findById(isbn); } public Uni<List<Book>> findByPrice(Integer price) { return list( "price > :price order by price desc", Parameters.with("price", price) ); }
ã©ã®ãããªã¡ãœãããå®çŸ©ãããŠããããšãããšãPanacheRepositoryBaseã€ã³ã¿ãŒãã§ãŒã¹ã®ãœãŒã¹ã³ãŒããèŠãã°ãããã®ã§ãã
ããã©ã«ãã¡ãœãããæžãåºããšããããªæãã§ããã
$ curl -s https://raw.githubusercontent.com/quarkusio/quarkus/2.6.3.Final/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/PanacheRepositoryBase.java | grep 'public default'
public default Uni<Mutiny.Session> getSession() {
public default Uni<Entity> persist(Entity entity) {
public default Uni<Entity> persistAndFlush(Entity entity) {
public default Uni<Void> delete(Entity entity) {
public default boolean isPersistent(Entity entity) {
public default Uni<Void> flush() {
public default Uni<Entity> findById(Id id) {
public default Uni<Entity> findById(Id id, LockModeType lockModeType) {
public default PanacheQuery<Entity> find(String query, Object... params) {
public default PanacheQuery<Entity> find(String query, Sort sort, Object... params) {
public default PanacheQuery<Entity> find(String query, Map<String, Object> params) {
public default PanacheQuery<Entity> find(String query, Sort sort, Map<String, Object> params) {
public default PanacheQuery<Entity> find(String query, Parameters params) {
public default PanacheQuery<Entity> find(String query, Sort sort, Parameters params) {
public default PanacheQuery<Entity> findAll() {
public default PanacheQuery<Entity> findAll(Sort sort) {
public default Uni<List<Entity>> list(String query, Object... params) {
public default Uni<List<Entity>> list(String query, Sort sort, Object... params) {
public default Uni<List<Entity>> list(String query, Map<String, Object> params) {
public default Uni<List<Entity>> list(String query, Sort sort, Map<String, Object> params) {
public default Uni<List<Entity>> list(String query, Parameters params) {
public default Uni<List<Entity>> list(String query, Sort sort, Parameters params) {
public default Uni<List<Entity>> listAll() {
public default Uni<List<Entity>> listAll(Sort sort) {
public default Multi<Entity> stream(String query, Object... params) {
public default Multi<Entity> stream(String query, Sort sort, Object... params) {
public default Multi<Entity> stream(String query, Map<String, Object> params) {
public default Multi<Entity> stream(String query, Sort sort, Map<String, Object> params) {
public default Multi<Entity> stream(String query, Parameters params) {
public default Multi<Entity> stream(String query, Sort sort, Parameters params) {
public default Multi<Entity> streamAll(Sort sort) {
public default Multi<Entity> streamAll() {
public default Uni<Long> count() {
public default Uni<Long> count(String query, Object... params) {
public default Uni<Long> count(String query, Map<String, Object> params) {
public default Uni<Long> count(String query, Parameters params) {
public default Uni<Long> deleteAll() {
public default Uni<Boolean> deleteById(Id id) {
public default Uni<Long> delete(String query, Object... params) {
public default Uni<Long> delete(String query, Map<String, Object> params) {
public default Uni<Long> delete(String query, Parameters params) {
public default Uni<Void> persist(Iterable<Entity> entities) {
public default Uni<Void> persist(Stream<Entity> entities) {
public default Uni<Void> persist(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) {
public default Uni<Integer> update(String query, Object... params) {
public default Uni<Integer> update(String query, Map<String, Object> params) {
public default Uni<Integer> update(String query, Parameters params) {
ãšããã§ãä»åäœæããã¡ãœããããããªã®ã§ãããããã¥ã¡ã³ããèŠãŠãããšJPQLãç°¡ç¥åããŠæžãããããªé°å²æ°ããããŸãã
// finding all living persons Uni<List<Person>> livingPersons = personRepository.list("status", Status.Alive); // counting all living persons Uni<Long> countAlive = personRepository.count("status", Status.Alive); // delete all living persons Uni<Long> deleteLivingOperation = personRepository.delete("status", Status.Alive); // set the name of all living persons to 'Mortal' Uni<Integer> updateOperation = personRepository.update("name = 'Mortal' where status = ?1", Status.Alive);
ããã¯ãã¯ãšãªãŒãšããŠæž¡ãããæååã®å é ãèŠãŠã以äžã®ã¯ã©ã¹ã§è£å®ããŠããããã§ãã
次ã¯ãèšå®ãè¡ããŸããæäœéã®èšå®ã¯ãReactive SQL Clientã䜿ããç¶æ ã«ããã°OKã§ãã
src/main/resources/application.properties
# Reactive SQL Client quarkus.datasource.db-kind=mysql quarkus.datasource.username=kazuhira quarkus.datasource.password=password # Reactive SQL Client quarkus.datasource.reactive.url=mysql://172.17.0.2:3306/practice?characterEncoding=utf8mb4&charset=utf8mb4&collation=utf8mb4_bin
ãã¹ãã³ãŒãã§ãäœ¿ãæ¹ã確èªã
src/test/java/org/littlewings/quarkus/hibernate/reactive/BookRepositoryTest.java
package org.littlewings.quarkus.hibernate.reactive; import java.util.List; import javax.inject.Inject; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @QuarkusTest public class BookRepositoryTest { @Inject BookRepository bookRepository; @Test public void gettingStarted() { Book book1 = Book.create("978-1492091721", "Reactive Systems in Java: Resilient, Event-Driven Architecture With Quarkus", 7069); Book book2 = Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6379); Book book3 = Book.create("978-1484260319", "Beginning Quarkus Framework: Build Cloud-Native Enterprise Java Applications and Microservices", 5039); // insert UniAssertSubscriber<Book> persistAssertSubscriber = bookRepository .persist(book1) .subscribe() .withSubscriber(UniAssertSubscriber.create()); // count UniAssertSubscriber<Long> countAssertSubscriber = bookRepository .count() .subscribe() .withSubscriber(UniAssertSubscriber.create()); countAssertSubscriber .awaitItem() .assertItem(1L); // insert iterable UniAssertSubscriber<Void> assertMultiInsertSubscriber = bookRepository .persist(List.of(book2, book3)) .subscribe() .withSubscriber(UniAssertSubscriber.create()); assertMultiInsertSubscriber.awaitItem().assertCompleted(); // count countAssertSubscriber = bookRepository .count() .subscribe() .withSubscriber(UniAssertSubscriber.create()); countAssertSubscriber .awaitItem() .assertItem(3L); // find UniAssertSubscriber<Integer> findAssertSubscriber = bookRepository .findByIsbn("978-1492091721") .onItem() .transform(book -> book.getPrice()) .subscribe() .withSubscriber(UniAssertSubscriber.create()); findAssertSubscriber .awaitItem() .assertCompleted() .assertItem(7069); // find query UniAssertSubscriber<List<Book>> findQueryAssertSubscriber = bookRepository .findByPrice(6000) .subscribe() .withSerializedSubscriber(UniAssertSubscriber.create()); List<Book> books = findQueryAssertSubscriber.awaitItem().getItem(); assertThat(books, hasSize(2)); assertThat(books.get(0).getTitle(), is("Reactive Systems in Java: Resilient, Event-Driven Architecture With Quarkus")); assertThat(books.get(0).getPrice(), is(7069)); assertThat(books.get(1).getTitle(), is("Quarkus Cookbook: Kubernetes-optimized Java Solutions")); assertThat(books.get(1).getPrice(), is(6379)); // delete UniAssertSubscriber<Boolean> deleteAssertSubscriber = bookRepository .deleteById("978-1492091721") .subscribe() .withSubscriber(UniAssertSubscriber.create()); deleteAssertSubscriber .awaitItem() .assertCompleted() .assertItem(true); // count countAssertSubscriber = bookRepository .count() .subscribe() .withSubscriber(UniAssertSubscriber.create()); countAssertSubscriber .awaitItem() .assertItem(2L); // delete all UniAssertSubscriber<Long> deleteAllAssertSubscriber = bookRepository .deleteAll() .subscribe() .withSubscriber(UniAssertSubscriber.create()); deleteAllAssertSubscriber.awaitItem(); // countïŒemptyïŒ countAssertSubscriber = bookRepository .count() .subscribe() .withSubscriber(UniAssertSubscriber.create()); countAssertSubscriber .awaitItem() .assertItem(0L); } }
ãã£ãšãããªæãã§ãããŸãããã£ã¡ã¯è¯ãã£ãã®ã§ããã
ãã©ã³ã¶ã¯ã·ã§ã³ã䜿ã£ãŠã¿ã
ç¶ããŠã¯ããã©ã³ã¶ã¯ã·ã§ã³ã䜿ã£ãŠã¿ãŸããçµæã ãèšããšãããã§ã®ããŒã«ããã¯ã®äœ¿ãæ¹ãããããããŸããã§ããã
Simplified Hibernate Reactive with Panache / Transactions
ãšãããããé²ããŠã¿ãŸãããã©ã³ã¶ã¯ã·ã§ã³å¢çã¯@ReactiveTransactionalã¢ãããŒã·ã§ã³ãä»äžãããšããããšãªã®ã§ããããä»äžãã
ã¡ãœãããæã€ãµãŒãã¹ã¯ã©ã¹ãäœæã
src/main/java/org/littlewings/quarkus/hibernate/reactive/BookService.java
package org.littlewings.quarkus.hibernate.reactive; import java.util.function.Function; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import io.quarkus.hibernate.reactive.panache.common.runtime.ReactiveTransactional; import io.smallrye.common.annotation.Blocking; import io.smallrye.mutiny.Uni; @ApplicationScoped public class BookService { @Inject BookRepository bookRepository; @ReactiveTransactional public <T> Uni<T> withTransaction(Function<BookRepository, Uni<T>> function) { return function.apply(bookRepository); } public <T> Uni<T> nonTransaction(Function<BookRepository, Uni<T>> function) { return function.apply(bookRepository); } }
ã¡ãœããã®äžèº«ãé¢åã«ãªã£ãã®ã§ãä»åã¯RepositoryãFunctionã§æäœãããããšã«ããŸããã
æ¯èŒã®ããã«ã@ReactiveTransactionalã¢ãããŒã·ã§ã³ãä»äžããŠããªãã¡ãœãããäœæã
ãã¹ãã³ãŒãã¯ãã¡ãã
src/test/java/org/littlewings/quarkus/hibernate/reactive/TransactionalTest.java
package org.littlewings.quarkus.hibernate.reactive; import java.time.Duration; import java.util.Arrays; import javax.inject.Inject; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.fail; @QuarkusTest public class TransactionalTest { @Inject BookService bookService; @Test public void commit() { Book book1 = Book.create("978-1492091721", "Reactive Systems in Java: Resilient, Event-Driven Architecture With Quarkus", 7069); Book book2 = Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6379); Book book3 = Book.create("978-1484260319", "Beginning Quarkus Framework: Build Cloud-Native Enterprise Java Applications and Microservices", 5039); Uni<Book> commit = bookService.withTransaction(bookRepository -> bookRepository .persist(book1) .onItem() .transformToUni(b -> bookRepository.persist(book2)) .onItem() .transformToUni(b -> bookRepository.persist(book3)) ); commit.await().indefinitely(); UniAssertSubscriber<Long> countSubscriber = bookService .withTransaction(bookRepository -> bookRepository.count()) .subscribe() .withSubscriber(UniAssertSubscriber.create()); countSubscriber .awaitItem() .assertItem(3L); bookService.withTransaction(bookRepository -> bookRepository.deleteAll()).await().indefinitely(); } @Test public void rollback() throws InterruptedException { Book book1 = Book.create("978-1492091721", "Reactive Systems in Java: Resilient, Event-Driven Architecture With Quarkus", 7069); Book book2 = Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6379); try { Uni<Book> rollback = bookService.withTransaction(bookRepository -> bookRepository .persist(book1) .onItem() .transformToUni(b -> bookRepository.persist(book2)) .onItem() .delayIt() .by(Duration.ofSeconds(3L)) .onItem() .transform(b -> { throw new RuntimeException("Oops!!"); }) ); rollback.await().indefinitely(); fail("fail"); } catch (Exception e) { assertThat(e.getCause(), instanceOf(RuntimeException.class)); assertThat(e.getCause().getMessage(), is("Oops!!")); assertThat(Arrays.asList(e.getSuppressed()), hasSize(1)); assertThat(e.getSuppressed()[0], instanceOf(IllegalStateException.class)); assertThat(e.getSuppressed()[0].getMessage(), startsWith("HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread")); } UniAssertSubscriber<Long> countSubscriber = bookService .withTransaction(bookRepository -> bookRepository.count()) .subscribe() .withSubscriber(UniAssertSubscriber.create()); countSubscriber .awaitFailure() .assertFailedWith(IllegalStateException.class); } @Test public void nonTransaction() { Book book1 = Book.create("978-1492091721", "Reactive Systems in Java: Resilient, Event-Driven Architecture With Quarkus", 7069); Book book2 = Book.create("978-1492062653", "Quarkus Cookbook: Kubernetes-optimized Java Solutions", 6379); try { Uni<Book> nonTransaction = bookService.nonTransaction(bookRepository -> bookRepository .persist(book1) .onItem() .transformToUni(b -> bookRepository.persist(book2)) .onItem() .delayIt() .by(Duration.ofSeconds(3L)) .onItem() .transform(b -> { throw new RuntimeException("Oops!!"); }) ); nonTransaction.await().indefinitely(); // throw RuntimeException fail("fail"); } catch (Exception e) { assertThat(e.getMessage(), is("Oops!!")); } UniAssertSubscriber<Long> countSubscriber = bookService .withTransaction(bookRepository -> bookRepository.count()) .subscribe() .withSubscriber(UniAssertSubscriber.create()); countSubscriber .awaitItem() .assertItem(2L); bookService.nonTransaction(bookRepository -> bookRepository.deleteAll()).await().indefinitely(); } }
ã³ããããããã¿ãŒã³ãšããããããã©ã³ã¶ã¯ã·ã§ã³ã䜿ããªããã¿ãŒã³ã¯ãŸã ããã®ã§ããã
ããŒã«ããã¯ããæ¹ãåããããªã³ãŒãã§çµæãawaitãããšãããã¯ãããªãšæããããããã®åŸã§ãã©ã³ã¶ã¯ã·ã§ã³ã䜿ãããšãããš
倱æãããã§ãããããããªãæãã«ãªããŸããã
ããšããã以äžè¿œããªããŠãããããªãšããæ°åã«ãªã£ãã®ã§ãããã§çµããã«ããŸãã
ãŸãšã
Quarkusã®Hibernate Reactive with Panache Extensionã詊ããŠã¿ãŸãããããã¹ãã³ãŒãã ãšãã©ã³ã¶ã¯ã·ã§ã³ãŸããã§èŠåŽããããšã«
ãªã£ãŠä»åã¯ã»ã©ã»ã©ã§ãããããšã«ããŸããã
REST APIãšããŠäœã£ãŠç¢ºèªããŠããããããã¯ãªããªãã£ããããããŸããããHibernate ReactiveãPanacheãã©ããããã®ãã¯
ããçšåºŠããã£ãæ°ãããã®ã§ãæ±ããªããŸããã®ãã¡ã«ããŸãããã