quarkus: Spring JpaRepository doesn't seems to work with ORM named persistence units

Describe the bug

I have set up two persistence units in application.properties (frontend and core) I have mapped packages using the property quarkus.hibernate-orm."<name>".packages accordingly. Then I want to use my entities through JpaRepository (quarkus-spring-data-jpa)

Then I get the following generic exception “The default datasource has not been properly configured.”

Expected behavior

It should work I presume

Using the EntityManager, it works fine!

Actual behavior

It throws exception, as follow

java.lang.IllegalStateException: The default datasource has not been properly configured. See https://quarkus.io/guides/datasource#jdbc-datasource for information on how to do that.
	at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.getEntityManager(AbstractJpaOperations.java:62)
	at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.getEntityManager(AbstractJpaOperations.java:51)
	at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.persist(AbstractJpaOperations.java:93)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl.save(Unknown Source)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_Subclass.save$$superforward1(Unknown Source)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_Subclass$$function$$43.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:49)
	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.proceed(AroundInvokeInvocationContext.java:50)
	at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:132)
	at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:103)
	at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38)
	at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:57)
	at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32)
	at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_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 it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_Subclass.save(Unknown Source)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl.save(Unknown Source)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_Subclass.save$$superforward1(Unknown Source)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_Subclass$$function$$52.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:49)
	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 it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_Subclass.save(Unknown Source)
	at it.ufficiodiscount.core.model.BrandRepository_fc01783303bb2bc9362ace4366341415b1c24525Impl_ClientProxy.save(Unknown Source)
	at org.acme.GreetingResource.makeBrand(GreetingResource.java:41)
	at org.acme.GreetingResource_Subclass.makeBrand$$superforward1(Unknown Source)
	at org.acme.GreetingResource_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:49)
	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 org.acme.GreetingResource_Subclass.makeBrand(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
	at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
	at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:408)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
	at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:91)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)
Resulted in: org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalStateException: The default datasource has not been properly configured. See https://quarkus.io/guides/datasource#jdbc-datasource for information on how to do that.
	at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:105)
	at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:359)
	at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:218)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:519)
	... 15 more

Further analysis

After some debug, I came up to this:

class AbstractJpaOperations:50

String persistentUnitName = entityToPersistenceUnit.get(clazzName);
--> persistentUnitName is null
--> entityToPersistenceUnit is empty

EntityManager

Using the EntityManager, everythings works fine!

How to Reproduce?

I have prepared a small project to reproduce the error.

quarkus-jpa-bug.zip

.\mvnw clean quarkus:dev

Call http://localhost:8080/hello/make-brand-hibernate to create a brand (core persistence unit) using EntityManager - it works Call http://localhost:8080/hello/make-brand-repository to create a brand (core persistence unit) using JpaRepository - it crashes

Same for Read methods http://localhost:8080/hello/show-brands-hibernate - it works http://localhost:8080/hello/show-brands-repository - it crashes

Output of uname -a or ver

No response

Output of java -version

java version “17.0.1” 2021-10-19 LTS

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.7.3.Final

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

quarkus-jpa-bug.zip

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (13 by maintainers)

Commits related to this issue

Most upvoted comments

At the end of this, I’d suggest to document clearly that Spring repositories only work with datasource.

Makes sense. Would you like to contribute that?

Yes @geoand , I will gladly do that.

Generated helpers like the “Jpa Repository” in Spring and PanacheRepository are additional conveniences for simple projects and fast developing prototypes.

TBH, I fail to see why spring repos wouldn’t be suitable for more complex, non-prototype apps?

Sorry I didn’t mean to say that they shouldn’t be used. More that it’s very likely in a larger project to need additional custom repositories anyway, which diminishes their relevance.

They still can be used of course, I just find them less so central to the design.

In terms of design pattern - what many fail to grasp is that the EntityManager is the repository. It’s a bit of a generic one, so there still is a need for the developer to organize custom queries an recurrent operations for certain areas of the business code in some “place” - now this place can be literally an extension of a Spring JPA Repository or a PanacheRepository but it’s not necessarily one. It also depends on design preference to organize stuff around a specific entity class rather than more fluid business needs.

(and incidentally JPA / Hibernate allows to declare named queries and such on the entity itself)

I generally prefer the “business needs” focus in organizing a large project as that decouples the 1:1 need to map a certain operation on a specific model class - although it’s totally fine if this is a composition and/or decoration of such core helpers the frameworks provide.

And in this sense I think what you suggest @lamemind-ud makes perfect sense. Introduce your own business layer, and feel free to use the various components the platform provides in there: so feel free to mix the use of PanacheRepository, EntityManager and/or JpaRepository. But perhaps limit the flavours to keep it simple 😃

Consider also that JpaRepository is a nice thing we provide for people who are familiar with Spring’s repository so they can keep coding with a familiar API - but if you’re new to these technologies you might as well prefer to skip this one: one less thing to learn, and not particularly relevant to you.