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]
Repository: https://github.com/BernardMenezes/spring-graalvm-demo
The application has two JPA EntityManager each one configured in their own configuration class:
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()));
}
}
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()));
}
}
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;
}
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> {
}
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();
}
}
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;
}
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With