Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IllegalArgumentException: Not a managed type: Entity in Spring GraalVM native compile

Problem

I'm trying to create an native executable of my application using Spring Boot 3 with GraalVM (sdk: java 22.3.2.r17-grl) native compile.

The application runs normally using standard SDK, but when I run the generated executable (result of $ ./gradlew nativeCompile) I'm receiving the following error:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'carController': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'carRepository': Not a managed type: class com.example.demo.domain.CarEntity
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:315) ~[na:na]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:258) ~[na:na]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:198) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:947) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1214) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1158) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[demo:6.0.9]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) ~[demo:6.0.9]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:941) ~[demo:6.0.9]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608) ~[demo:6.0.9]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[demo:3.0.7]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[demo:3.0.7]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[demo:3.0.7]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[demo:3.0.7]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[demo:3.0.7]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[demo:3.0.7]
        at com.example.demo.DemoApplication.main(DemoApplication.java:12) ~[demo:na]

About application

Repository: https://github.com/BernardMenezes/spring-graalvm-demo

The application has two JPA EntityManager each one configured in their own configuration class:

N1DataSourceConfig

package com.example.demo.config;

import com.example.demo.domain.CarEntity;
import com.example.demo.repo.CarRepository;
import com.zaxxer.hikari.HikariDataSource;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Objects;

@Configuration
@EnableJpaRepositories(
        basePackageClasses = {CarRepository.class},
        entityManagerFactoryRef = "n1EntityManagerFactory",
        transactionManagerRef = "n1TransactionManager"
)
@RequiredArgsConstructor
@EnableTransactionManagement
public class N1DataSourceConfig {

    @Primary
    @Bean
    public DataSource n1DataSource() {

        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setUsername("postgres");
        hikariDataSource.setPassword("postgres");
        hikariDataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/n1");
        hikariDataSource.setMaximumPoolSize(10);
        return hikariDataSource;
    }

    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean n1EntityManagerFactory(EntityManagerFactoryBuilder builder) {

        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");
        properties.put("hibernate.physical_naming_strategy", "org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
        properties.put("hibernate.globally_quoted_identifiers", true);

        return builder
                .dataSource(n1DataSource())
                .persistenceUnit("n1")
                .packages(
                        CarEntity.class.getPackage().getName()
                )
                .properties(properties)
                .build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager n1TransactionManager(
            final @Qualifier("n1EntityManagerFactory") LocalContainerEntityManagerFactoryBean n1EntityManagerFactory) {
        return new JpaTransactionManager(Objects.requireNonNull(n1EntityManagerFactory.getObject()));
    }
}

N2DataSourceConfig

package com.example.demo.config;

@Configuration
@EnableJpaRepositories(
        basePackageClasses = {},
        entityManagerFactoryRef = "n2EntityManagerFactory",
        transactionManagerRef = "n2TransactionManager"
)
@RequiredArgsConstructor
@EnableTransactionManagement
public class N2DataSourceConfig {

    @Bean
    public DataSource n2DataSource() {

        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setUsername("postgres");
        hikariDataSource.setPassword("postgres");
        hikariDataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/n2");
        hikariDataSource.setMaximumPoolSize(10);
        return hikariDataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean n2EntityManagerFactory(EntityManagerFactoryBuilder builder) {

        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");
        properties.put("hibernate.physical_naming_strategy", "org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
        properties.put("hibernate.globally_quoted_identifiers", true);

        return builder
                .dataSource(n2DataSource())
                .persistenceUnit("n2")
                .packages(
                        RevisionEntity.class.getPackage().getName()
                )
                .properties(properties)
                .build();
    }

    @Bean
    public PlatformTransactionManager n2TransactionManager(
            final @Qualifier("n1EntityManagerFactory") LocalContainerEntityManagerFactoryBean n1EntityManagerFactory) {
        return new JpaTransactionManager(Objects.requireNonNull(n1EntityManagerFactory.getObject()));
    }
}

CarEntity

package com.example.demo.domain;

import jakarta.persistence.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;

import java.time.ZonedDateTime;
import java.util.UUID;

@Entity
@Table(name = "car")
public class CarEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @CreatedDate
    @Column(columnDefinition = "timestamp")
    private ZonedDateTime createdAt;

    @LastModifiedDate
    @Column(columnDefinition = "timestamp")
    private ZonedDateTime lastModified;

    @Column(unique = true)
    private String name;

    @Version
    private Integer version;

}

CarRepository

package com.example.demo.repo;

import com.example.demo.domain.CarEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.UUID;

@Repository
public interface CarRepository extends JpaRepository<CarEntity, UUID> {
}

CarController

package com.example.demo.api;

import com.example.demo.domain.CarEntity;
import com.example.demo.repo.CarRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
public class CarController {

    private final CarRepository carRepository;

    @GetMapping("/car")
    public List<CarEntity> getCar() {
        return carRepository.findAll();
    }

}

RevisionEntity

package com.example.demo.domain;

import jakarta.persistence.*;
import lombok.Setter;

import java.util.Date;

@Entity
@Table(name = "revision")
public class RevisionEntity {

    @Id
    @GeneratedValue
    private long id;

    @Temporal(TemporalType.TIMESTAMP)
    private Date timestamp;

    @Setter
    private String context;

}

DemoApplication

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.7'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'org.graalvm.buildtools.native' version '0.9.20'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    implementation 'org.springframework.boot:spring-boot-starter-web'

    runtimeOnly 'org.postgresql:postgresql'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}


I was expecting to be able to run the application using the native executable, just as I run with standard JDK.

like image 567
Bernard Menezes Avatar asked Jan 17 '26 18:01

Bernard Menezes


1 Answers

Your LocalContainerEntityManagerFactoryBean is missing the PersistenceManagedTypes parameter. I've created a sample application how to use multiple databases with spring boot and native images here, which also shows how to setup a custom datasource. The usage of PersistenceManagedTypes is also documented in spring-docs. Took me a while to figure this out, too.

like image 56
Harry Developer Avatar answered Jan 19 '26 17:01

Harry Developer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!