You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quarkus中Hibernate的EntityManager注入返回null问题求助

问题描述

我正在搭建一个基础的Quarkus+Hibernate应用,将简单的Film实体持久化到Sakila测试数据库。遵循官方指南操作,但FilmService中注入的EntityManager始终返回null。代码的大部分内容在非Quarkus的Hibernate应用中使用无问题,例如Actor和Film实体类。已单独测试过数据库配置,确认连接无异常。

相关配置与代码如下:

application.properties

quarkus.datasource.db-kind = mysql
quarkus.datasource.username = root
quarkus.datasource.password = sakila
quarkus.datasource.jdbc.url = jdbc:mysql://localhost:3306/sakila

Main.class

package com.example;

public class Main {

    public static void main(String[] args) {
        FilmService filmService = new FilmService(); 
    
        filmService.createFilm("Sagan om ringen");
    }
}

FilmService.class

package com.example;

import javax.enterprise.context.ApplicationScoped; 
import javax.inject.Inject; 
import javax.persistence.EntityManager; 
import javax.transaction.Transactional;

@ApplicationScoped public class FilmService { 

    @Inject EntityManager em;

    @Transactional
    public void createFilm(String giftDescription) {
        Film film = new Film();
        film.setFilmId((short) 12312);
        film.setTitle(giftDescription);
        if (em != null) {
            em.persist(film);
        } else {
            System.out.println("EM is null!!");
        }
    }
}

Film.class

package com.example;

import javax.persistence.*; 
import java.math.BigDecimal; 
import java.sql.Timestamp; 
import java.util.HashSet; 
import java.util.Set;

@Entity 
public class Film {

    @Id
    @Column(name = "film_id", columnDefinition = "SMALLINT UNSIGNED")
    private short filmId;
    
    @Basic
    @Column(name = "title", columnDefinition = "VARCHAR(255)")
    private String title;
       
    @Basic
    @Column(name = "description", columnDefinition = "TEXT")
    private String description;

    @Basic
    @Column(name = "rental_duration", columnDefinition = "YEAR")
    private short rentalDuration;

    @Basic
    @Column(name = "language_id", columnDefinition = "SMALLINT UNSIGNED")
    private short languageId;

    @Basic
    @Column(name = "rental_rate", columnDefinition = "DECIMAL(4,2)")
    private BigDecimal rentalRate;
    
    @Basic    
    @Column(name = "length", columnDefinition = "SMALL UNSIGNED")
    private Short length;
    
    @Basic
    @Column(name = "rating", columnDefinition = "enum('G','PG','PG-13','R','NC-17')")          
    private String rating;
    
    @Basic    
    @Column(name = "replacement_cost")  
    private BigDecimal replacementCost;
    
    @Basic
    @Column(name = "last_update") 
    private Timestamp lastUpdate;
    
    public short getFilmId() {
        return filmId;
    }
    
    public void setFilmId(short filmId) {
        this.filmId = filmId;
    }
    
    public String getTitle() {
        return title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    
    public short getLanguageId() {
        return languageId;
    }
    
    public void setLanguageId(short languageId) {
        this.languageId = languageId;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public short getRentalDuration() {
        return rentalDuration;
    }
    
    public void setRentalDuration(short rentalDuration) {
        this.rentalDuration = rentalDuration;
    }
    
    public String getRating() { return rating; }
    
    public void setRating(String rating) { this.rating = rating; }
    
    public BigDecimal getRentalRate() {
        return rentalRate;
    }
    
    public void setRentalRate(BigDecimal rentalRate) {
        this.rentalRate = rentalRate;
    }
    
    public Short getLength() {
        return length;
    }
    
    public void setLength(Short length) {
        this.length = length;
    }
    
    public BigDecimal getReplacementCost() {
        return replacementCost;
    }
    
    public void setReplacementCost(BigDecimal replacementCost) {
        this.replacementCost = replacementCost;
    }
    public Timestamp getLastUpdate() {
        return lastUpdate;
    }
    
    public void setLastUpdate(Timestamp lastUpdate) {
        this.lastUpdate = lastUpdate;
    }
    
    @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinTable(
            name = "film_actor",
            joinColumns = { @JoinColumn(name = "film_id") },
            inverseJoinColumns = { @JoinColumn(name = "actor_id") }
    )
    private Set<Actor> actors = new HashSet<>();
    
    public void addActor(Actor actor) {
        actors.add(actor);
        actor.getFilms().add(this);
    }
    
    public void removeActor(Actor actor) {
        actors.remove(actor);
        actor.getFilms().remove(this);
    }
    
    public Set<Actor> getActors() {
        return this.actors; 
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
    
        Film film = (Film) o;
    
        if (filmId != film.filmId) return false;
        if (rentalDuration != film.rentalDuration) return false;
        if (title != null ? !title.equals(film.title) : film.title != null) return false;
        if (description != null ? !description.equals(film.description) : film.description != null) return false;
        if (rentalRate != null ? !rentalRate.equals(film.rentalRate) : film.rentalRate != null) return false;
        if (length != null ? !length.equals(film.length) : film.length != null) return false;
        if (replacementCost != null ? !replacementCost.equals(film.replacementCost) : film.replacementCost != null)
            return false;
        if (lastUpdate != null ? !lastUpdate.equals(film.lastUpdate) : film.lastUpdate != null) return false;
    
        return true;
    }
    
    @Override
    public int hashCode() {
        int result = (int) filmId;
        result = 31 * result + (title != null ? title.hashCode() : 0);
        result = 31 * result + (description != null ? description.hashCode() : 0);
        result = 31 * result + (int) rentalDuration;
        result = 31 * result + (rentalRate != null ? rentalRate.hashCode() : 0);
        result = 31 * result + (length != null ? length.hashCode() : 0);
        result = 31 * result + (replacementCost != null ? replacementCost.hashCode() : 0);
        result = 31 * result + (lastUpdate != null ? lastUpdate.hashCode() : 0);
        return result;
    }
}

Actor.class

package com.example;

import javax.persistence.*; 
import java.util.HashSet; 
import java.util.Objects; 
import java.util.Set;

@Entity 
public class Actor {

    public Actor() {}
    
    public Actor(Integer actorId) {
        this.actorId = actorId;
    }
    
    public Actor(Integer actorId, String firstName, String lastName) {
        this.actorId = actorId;
        this.firstName = firstName;
        this.lastName = lastName; 
    }
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "actor_id", nullable = false, updatable = false, columnDefinition = "smallint(5)")
    private Integer actorId;
    
    @Column(name = "first_name", nullable = false, columnDefinition = "varchar(45)")
    private String firstName;
    
    @Column(name = "last_name", nullable = false, columnDefinition = "varchar(45)")
    private String lastName;
    
    @ManyToMany(mappedBy = "actors")
    private Set<Film> films = new HashSet<>();
    
    public Integer getActorId() {
        return actorId;
    }
    
    public void setActorId(Integer actorId) {
        this.actorId = actorId;
    }
    
    public String getFirstName() {
        return firstName;
    }
    
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    
    public String getLastName() {
        return lastName;
    }
    
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    
    public Set<Film> getFilms() {
        return films;
    }
    
    
    @Override
    public String toString() {
        return "Actor{" +
                "actorId=" + actorId +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
    
        Actor actor = (Actor) o;
    
        if (!Objects.equals(actorId, actor.actorId)) return false;
        if (!Objects.equals(firstName, actor.firstName)) return false;
        return Objects.equals(lastName, actor.lastName);
    }
    
    @Override
    public int hashCode() {
        int result = actorId != null ? actorId.hashCode() : 0;
        result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
        return result;
    }
}

解决方案

1. 核心问题:手动实例化CDI Bean

你在Main类中直接通过new FilmService()创建实例,这会绕过Quarkus的依赖注入容器。只有由容器管理的Bean实例,才能触发@Inject注解的注入逻辑,手动创建的实例无法获取到EntityManager。

修正方式:通过容器获取Bean实例

可以使用@QuarkusMain注解实现符合Quarkus规范的启动类,让容器注入FilmService:

package com.example;

import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import javax.inject.Inject;

@QuarkusMain
public class Main implements QuarkusApplication {

    @Inject
    FilmService filmService;

    @Override
    public int run(String... args) throws Exception {
        filmService.createFilm("Sagan om ringen");
        Quarkus.exit();
        return 0;
    }

    public static void main(String[] args) {
        Quarkus.run(Main.class, args);
    }
}

或者使用启动事件触发逻辑,无需手动编写Main类:

package com.example;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import io.quarkus.runtime.StartupEvent;

@ApplicationScoped
public class AppInitializer {

    @Inject
    FilmService filmService;

    void onStart(@Observes StartupEvent event) {
        filmService.createFilm("Sagan om ringen");
    }
}

2. 验证依赖完整性

确保项目依赖中包含Quarkus Hibernate ORM和MySQL JDBC驱动:

Maven(pom.xml)

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>

Gradle(build.gradle)

implementation 'io.quarkus:quarkus-hibernate-orm'
implementation 'io.quarkus:quarkus-jdbc-mysql'

3. 确认实体包扫描配置

如果实体类不在当前包或其子包下,需要在application.properties中指定扫描路径:

quarkus.hibernate-orm.packages=com.example

4. 检查事务注解有效性

@Transactional注解依赖Quarkus的事务管理器,确保quarkus-narayana-jta依赖已被引入(该依赖会被quarkus-hibernate-orm自动关联)。


内容的提问来源于stack exchange,提问作者Julia Gustafsson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:01:18