aileftech / snap-admin

A plug-and-play, auto-generated CRUD database admin panel for Spring Boot apps
MIT License
251 stars 18 forks source link

Entity is not recognized as managed: "java.lang.IllegalArgumentException: Not a managed type" #1

Closed adriandeleon closed 11 months ago

adriandeleon commented 11 months ago

Hello, I'm getting a java.lang.IllegalArgumentException: Not a managed type: class com.grokthecode.damcheck.entities.DamEntity Exception for a entity named DamEntity, but it does have the @Entity annotation.

Here is the relevant stack trace:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dbAdmin' defined in URL [jar:file:/C:/Users/adria/.m2/repository/tech/ailef/spring-boot-db-admin/0.1.2/spring-boot-db-admin-0.1.2.jar!/tech/ailef/dbadmin/external/DbAdmin.class]: Failed to instantiate [tech.ailef.dbadmin.external.DbAdmin]: Constructor threw exception
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:321) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:309) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1189) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:942) ~[spring-context-6.0.12.jar:6.0.12]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608) ~[spring-context-6.0.12.jar:6.0.12]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298) ~[spring-boot-3.1.4.jar:3.1.4]
    at com.grokthecode.damcheck.DamCheckApplication.main(DamCheckApplication.java:14) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-3.1.4.jar:3.1.4]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [tech.ailef.dbadmin.external.DbAdmin]: Constructor threw exception
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:224) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:110) ~[spring-beans-6.0.12.jar:6.0.12]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:318) ~[spring-beans-6.0.12.jar:6.0.12]
    ... 22 common frames omitted
Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: Not a managed type: class com.grokthecode.damcheck.entities.DamEntity
    at tech.ailef.dbadmin.external.DbAdmin.processBeanDefinition(DbAdmin.java:149) ~[spring-boot-db-admin-0.1.2.jar:0.1.2]
    at tech.ailef.dbadmin.external.DbAdmin.<init>(DbAdmin.java:65) ~[spring-boot-db-admin-0.1.2.jar:0.1.2]
    at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211) ~[spring-beans-6.0.12.jar:6.0.12]
    ... 24 common frames omitted 

And here is the partial code for the entity:

import com.grokthecode.damcheck.tools.DamTool;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.Validate;

import java.time.LocalDate;

/**
 * The Dam entity.
 */
@Getter
@Setter
@Table(name = "dams")
@Entity
public class DamEntity {

I am using Lombok, maybe that's causing noise?

I'm using Spring boot 3.1.4, Amazon Correto JDK 17 and spring-boot-database-admin 0.1.2

Thank you.

aileftech commented 11 months ago

Thanks for reporting this issue. I will look into it soon and keep you updated.

As for Lombok, I have not yet tested it and I guess there's the possibility that sometimes it intereferes, but I don't think it's the issue here.

aileftech commented 11 months ago

I was able to reproduce the issue with version 0.1.2 but not with the latest commit version. I've released 0.1.3 on Maven and it should be available in a few hours (or you can clone the repo). As I am not sure about the cause of the issue yet, please let me know if still persists on your end.

agxs commented 11 months ago

I get the same problem with 0.1.3. My entity setup is really simple:

@Entity
@Table(name = "users")
public class ChatUser {

No lombok is involved.

agxs commented 11 months ago

Just noticed I'm using OffsetDateTime which isn't in your list of supported types.

aileftech commented 11 months ago

Hey @agxs, thanks for chiming in!

Using an unsupported type should result in an error at startup, like the following:

tech.ailef.dbadmin.external.exceptions.DbAdminException: Unable to determine fieldType for class java.time.OffsetDateTime

Since this is not happening it means the "Not a managed type" error occurs earlier and is not related to the OffsetDateTime. In other words, you would get the OffsetDateTime error anyway after fixing the first one.

Could you tell me what is your data source and how it's configured?

PS: I can look into adding OffsetDateTime support as well, I'll open another issue about it (#7).

aileftech commented 11 months ago

In particuar, could anybody who is getting this error look at the application.properties file for the test project and see if there's any possibly relevant difference, particularly in regarding the way the data source is set up?

agxs commented 11 months ago

Running in the debugger it looks like the list of managed entities stored in the JPA Metamodel and the class passed via the CustomJpaRepository aren't equal (despite being the same package and classname). Looks like the registered classloader for each Class is different.

Not sure the above is relevant after following through other sections of the code :(

agxs commented 11 months ago

Thanks for the quick response @aileftech . My application.properties setup is:

spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/<db>
spring.datasource.username=<username>
spring.datasource.password=<password>

spring.flyway.url=${spring.datasource.url}
spring.flyway.user=${spring.datasource.username}
spring.flyway.password=${spring.datasource.password}
spring.flyway.baseline-on-migrate=false

spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
aileftech commented 11 months ago

I think I have found the issue, or at least I have an idea on how to investigate. Could you please add the property:

dbadmin.testMode=true

and see if it runs? The difference is that it will use an in-memory database for the internal storage, so it will not persist across restarts. A problem occurs when the persistent data source is used instead, if my intuition is correct.

In any case I am able to reproduce the issue now with the latest version as well so I'll be working on it.

aileftech commented 11 months ago

Upon further inspection, this seems to have nothing to do with testMode, but rather with the way the library is loaded. It works when I run from the IDE with a not yet mvn installed version of the library. At this point it doesn't work from the command line with mvn spring-boot:run because it doesn't find the dependency. After mvn install it runs from the command line but gives the exception.

Could this be the ClassLoader related issue you where mentioning? Not asking rethorically, I don't know much about that.

aileftech commented 11 months ago

I believe I have finally solved this issue.

The problem is the inclusion of spring-boot-devtools I had in my pom.xml. When using Devtools there's a custom RestartClassLoader to perform live-reloading. This results in the EntityManager and the Entity class having a different ClassLoader and thus the EntityManager not recognizing the entity as a managed type. I'm not sure why this happens only when using the command line (when using the IDE both classes have the same RestartClassLoader).

In any case, the solution for now is to not include devtools. I've removed it from my pom.xml and you should remove it from yours as well in order for the application to run.

If anybody can confirm this solves the issue it would be great. I'll open another issue in order to see if I can work around this problem and make the project work with Devtools as well.

aileftech commented 11 months ago

I have released a fixed 0.1.4 version on Maven which should be available soon. Waiting for some feedback to mark the issue as closed.

agxs commented 11 months ago

I just saw your #8 issue and remembered I have the Spring DevTools artifect loaded in my project. Removing this dependency worked (Well, now I get the error about OffsetDateTime not being recognised but that's covered in #7 😄 )

EDIT - ah yes, just noticed your update above, my browser tab hadn't updated when I added this comment.

aileftech commented 11 months ago

Thank you so much! I'm glad it works now, as this one was a bit tricky to debug (GPT4 has been very useful to troubleshoot with).

I'm already working on OffsetDateTime as well, but it requires a bit of a refactor for the code to be more flexible so I can also add other types later more easily.