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
相关产品推荐
相关产品推荐

