我正在尝试使用 Spring Boot 3 和 GraalVM(sdk:java 22.3.2.r17-grl)本机编译来创建应用程序的本机可执行文件。
应用程序使用标准 SDK 正常运行,但是当我运行生成的可执行文件(
$ ./gradlew nativeCompile
的结果)时,我收到以下错误:
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]
存储库:https://github.com/BernardMenezes/spring-graalvm-demo
该应用程序有两个 JPA EntityManager,每个都在自己的配置类中配置:
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()
}
我希望能够使用本机可执行文件运行应用程序,就像使用标准 JDK 运行一样。
您的 LocalContainerEntityManagerFactoryBean 缺少 PersistenceManagedTypes 参数。我创建了一个示例应用程序,如何通过 Spring Boot 和本机映像使用多个数据库here,它还展示了如何设置自定义数据源。 PersistenceManagedTypes 的用法也记录在 spring-docs 中。我也花了一段时间才弄清楚这一点。
我遵循了大部分答案,但没有解决我的问题。
就我而言,答案很简单:
Project details:
Spring Boot, Maven, Java 17, Multi-Module
我必须改变
形式:
import javax.persistence.*;
至:
import jakarta.persistence.*;