Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow assigning entities to the Hibernate Reactive PU explicitly #28632

Merged
merged 2 commits into from Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -1230,7 +1230,7 @@ private void enhanceEntities(final JpaModelBuildItem jpaModel,
}
}

private static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig,
public static Map<String, Set<String>> getModelClassesAndPackagesPerPersistenceUnits(HibernateOrmConfig hibernateOrmConfig,
JpaModelBuildItem jpaModel, IndexView index, boolean enableDefaultPersistenceUnit) {
Map<String, Set<String>> modelClassesAndPackagesPerPersistenceUnits = new HashMap<>();

Expand Down
Expand Up @@ -18,6 +18,7 @@
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import javax.persistence.SharedCacheMode;
Expand Down Expand Up @@ -111,7 +112,7 @@ public void build(RecorderContext recorderContext,

@BuildStep
public void buildReactivePersistenceUnit(
HibernateOrmConfig hibernateOrmConfig,
HibernateOrmConfig hibernateOrmConfig, CombinedIndexBuildItem index,
DataSourcesBuildTimeConfig dataSourcesBuildTimeConfig,
List<PersistenceXmlDescriptorBuildItem> persistenceXmlDescriptors,
ApplicationArchivesBuildItem applicationArchivesBuildItem,
Expand Down Expand Up @@ -154,7 +155,7 @@ public void buildReactivePersistenceUnit(
final String dbKind = dbKindOptional.get();
HibernateOrmConfigPersistenceUnit persistenceUnitConfig = hibernateOrmConfig.defaultPersistenceUnit;
ParsedPersistenceXmlDescriptor reactivePU = generateReactivePersistenceUnit(
hibernateOrmConfig, persistenceUnitConfig, jpaModel,
hibernateOrmConfig, index, persistenceUnitConfig, jpaModel,
dbKind, applicationArchivesBuildItem, launchMode.getLaunchMode(),
systemProperties, nativeImageResources, hotDeploymentWatchedFiles, dbKindDialectBuildItems);

Expand Down Expand Up @@ -202,8 +203,16 @@ PersistenceProviderSetUpBuildItem setUpPersistenceProviderAndWaitForVertxPool(Hi
* - Any JDBC-only configuration settings are removed
* - If we ever add any Reactive-only config settings, they can be set here
*/
// TODO this whole method is really just a hack that duplicates
// io.quarkus.hibernate.orm.deployment.HibernateOrmProcessor.handleHibernateORMWithNoPersistenceXml
// and customizes it for Hibernate Reactive.
// we should work on a way to merge the two methods while still having some behavior specific to
// HR/ORM, because it's likely the HR implementation is missing some features,
// and we've seen in the past that features we add to handleHibernateORMWithNoPersistenceXml
// tend not to be added here.
// See https://github.com/quarkusio/quarkus/issues/28629.
private static ParsedPersistenceXmlDescriptor generateReactivePersistenceUnit(
HibernateOrmConfig hibernateOrmConfig,
HibernateOrmConfig hibernateOrmConfig, CombinedIndexBuildItem index,
HibernateOrmConfigPersistenceUnit persistenceUnitConfig,
JpaModelBuildItem jpaModel,
String dbKind,
Expand All @@ -230,7 +239,26 @@ private static ParsedPersistenceXmlDescriptor generateReactivePersistenceUnit(
desc.setTransactionType(PersistenceUnitTransactionType.RESOURCE_LOCAL);
desc.getProperties().setProperty(AvailableSettings.DIALECT, dialect);
desc.setExcludeUnlistedClasses(true);
desc.addClasses(new ArrayList<>(jpaModel.getAllModelClassNames()));

Map<String, Set<String>> modelClassesAndPackagesPerPersistencesUnits = HibernateOrmProcessor
.getModelClassesAndPackagesPerPersistenceUnits(hibernateOrmConfig, jpaModel, index.getIndex(), true);
Set<String> nonDefaultPUWithModelClassesOrPackages = modelClassesAndPackagesPerPersistencesUnits.entrySet().stream()
.filter(e -> !PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME.equals(e.getKey()) && !e.getValue().isEmpty())
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
if (!nonDefaultPUWithModelClassesOrPackages.isEmpty()) {
// Not supported yet; see https://github.com/quarkusio/quarkus/issues/21110
LOG.warnf("Entities are affected to non-default Hibernate Reactive persistence units %s."
+ " Since Hibernate Reactive only works with the default persistence unit, those entities will be ignored.",
nonDefaultPUWithModelClassesOrPackages);
}
Set<String> modelClassesAndPackages = modelClassesAndPackagesPerPersistencesUnits
.getOrDefault(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME, Collections.emptySet());
if (modelClassesAndPackages.isEmpty()) {
LOG.warnf("Could not find any entities affected to the Hibernate Reactive persistence unit.");
} else {
desc.addClasses(new ArrayList<>(modelClassesAndPackages));
}

// The storage engine has to be set as a system property.
if (persistenceUnitConfig.dialect.storageEngine.isPresent()) {
Expand Down
@@ -0,0 +1,74 @@
package io.quarkus.hibernate.reactive.singlepersistenceunit;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.logging.Formatter;
import java.util.logging.Level;

import javax.inject.Inject;

import org.hibernate.reactive.mutiny.Mutiny;
import org.jboss.logmanager.formatters.PatternFormatter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.excludedpackage.ExcludedEntity;
import io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.packageincludedthroughannotation.EntityIncludedThroughPackageAnnotation;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.vertx.RunOnVertxContext;
import io.quarkus.test.vertx.UniAsserter;
import io.smallrye.mutiny.Uni;

public class SinglePersistenceUnitPackageAnnotationTest {

private static final Formatter LOG_FORMATTER = new PatternFormatter("%s");

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addPackage(EntityIncludedThroughPackageAnnotation.class.getPackage().getName())
.addPackage(ExcludedEntity.class.getPackage().getName()))
.withConfigurationResource("application.properties")
// Expect a warning on startup
.setLogRecordPredicate(
record -> record.getMessage().contains("Could not find a suitable persistence unit for model classes"))
.assertLogRecords(records -> assertThat(records)
.as("Warnings on startup")
.hasSize(1)
.element(0).satisfies(record -> {
assertThat(record.getLevel()).isEqualTo(Level.WARNING);
assertThat(LOG_FORMATTER.formatMessage(record))
.contains(
io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.excludedpackage.ExcludedEntity.class
.getName());
}));

@Inject
Mutiny.SessionFactory sessionFactory;

@Test
@RunOnVertxContext
public void testIncluded(UniAsserter asserter) {
EntityIncludedThroughPackageAnnotation entity = new EntityIncludedThroughPackageAnnotation("default");
asserter.assertThat(
() -> persist(entity).chain(() -> find(EntityIncludedThroughPackageAnnotation.class, entity.id)),
retrievedEntity -> assertThat(retrievedEntity.name).isEqualTo(entity.name));
}

@Test
@RunOnVertxContext
public void testExcluded(UniAsserter asserter) {
ExcludedEntity entity = new ExcludedEntity("gsmet");
asserter.assertFailedWith(() -> persist(entity), t -> {
assertThat(t).hasMessageContaining("Unknown entity");
});
}

private Uni<Void> persist(Object entity) {
return sessionFactory.withTransaction(s -> s.persist(entity));
}

private <T> Uni<T> find(Class<T> entityClass, Object id) {
return sessionFactory.withSession(s -> s.find(entityClass, id));
}
}
@@ -0,0 +1,74 @@
package io.quarkus.hibernate.reactive.singlepersistenceunit;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.logging.Formatter;
import java.util.logging.Level;

import javax.inject.Inject;

import org.hibernate.reactive.mutiny.Mutiny;
import org.jboss.logmanager.formatters.PatternFormatter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.excludedpackage.ExcludedEntity;
import io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.packageincludedthroughconfig.EntityIncludedThroughPackageConfig;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.vertx.RunOnVertxContext;
import io.quarkus.test.vertx.UniAsserter;
import io.smallrye.mutiny.Uni;

public class SinglePersistenceUnitPackageConfigurationTest {

private static final Formatter LOG_FORMATTER = new PatternFormatter("%s");

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addPackage(EntityIncludedThroughPackageConfig.class.getPackage().getName())
.addPackage(ExcludedEntity.class.getPackage().getName()))
.withConfigurationResource("application.properties")
.overrideConfigKey("quarkus.hibernate-orm.packages",
EntityIncludedThroughPackageConfig.class.getPackage().getName())
// Expect a warning on startup
.setLogRecordPredicate(
record -> record.getMessage().contains("Could not find a suitable persistence unit for model classes"))
.assertLogRecords(records -> assertThat(records)
.as("Warnings on startup")
.hasSize(1)
.element(0).satisfies(record -> {
assertThat(record.getLevel()).isEqualTo(Level.WARNING);
assertThat(LOG_FORMATTER.formatMessage(record))
.contains(ExcludedEntity.class.getName());
}));

@Inject
Mutiny.SessionFactory sessionFactory;

@Test
@RunOnVertxContext
public void testIncluded(UniAsserter asserter) {
EntityIncludedThroughPackageConfig entity = new EntityIncludedThroughPackageConfig("default");
asserter.assertThat(
() -> persist(entity).chain(() -> find(EntityIncludedThroughPackageConfig.class, entity.id)),
retrievedEntity -> assertThat(retrievedEntity.name).isEqualTo(entity.name));
}

@Test
@RunOnVertxContext
public void testExcluded(UniAsserter asserter) {
ExcludedEntity entity = new ExcludedEntity("gsmet");
asserter.assertFailedWith(() -> persist(entity), t -> {
assertThat(t).hasMessageContaining("Unknown entity");
});
}

private Uni<Void> persist(Object entity) {
return sessionFactory.withTransaction(s -> s.persist(entity));
}

private <T> Uni<T> find(Class<T> entityClass, Object id) {
return sessionFactory.withSession(s -> s.find(entityClass, id));
}
}
@@ -0,0 +1,24 @@
package io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.excludedpackage;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class ExcludedEntity {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "excludedSeq")
public long id;

public String name;

public ExcludedEntity() {
}

public ExcludedEntity(String name) {
this.name = name;
}

}
@@ -0,0 +1,24 @@
package io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.packageincludedthroughannotation;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class EntityIncludedThroughPackageAnnotation {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "includedSeq")
public long id;

public String name;

public EntityIncludedThroughPackageAnnotation() {
}

public EntityIncludedThroughPackageAnnotation(String name) {
this.name = name;
}

}
@@ -0,0 +1,4 @@
@PersistenceUnit(PersistenceUnit.DEFAULT)
package io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.packageincludedthroughannotation;

import io.quarkus.hibernate.orm.PersistenceUnit;
@@ -0,0 +1,24 @@
package io.quarkus.hibernate.reactive.singlepersistenceunit.entityassignment.packageincludedthroughconfig;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class EntityIncludedThroughPackageConfig {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "includedSeq")
public long id;

public String name;

public EntityIncludedThroughPackageConfig() {
}

public EntityIncludedThroughPackageConfig(String name) {
this.name = name;
}

}