Skip to content

[#487] One SessionFactory per test class #683

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

Merged
merged 14 commits into from
Apr 6, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions hibernate-reactive-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ dependencies {
implementation "io.vertx:vertx-sql-client:${vertxVersion}"

// Testing
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'

testImplementation 'org.assertj:assertj-core:3.13.2'
testImplementation "io.vertx:vertx-unit:${vertxVersion}"

Expand Down Expand Up @@ -94,9 +90,6 @@ tasks.withType(Test) {
// Example:
// gradle test -Pdb=MySQL
test {
// Enable JUnit 5
useJUnitPlatform()

def selectedDb = project.hasProperty( 'db' )
? project.getProperty( 'db' )
: 'PostgreSQL'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
*/
package org.hibernate.reactive;

import io.smallrye.mutiny.Uni;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.Timeout;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
Expand All @@ -21,21 +20,33 @@
import org.hibernate.reactive.containers.DatabaseConfiguration.DBType;
import org.hibernate.reactive.mutiny.Mutiny;
import org.hibernate.reactive.pool.ReactiveConnection;
import org.hibernate.reactive.pool.ReactiveConnectionPool;
import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder;
import org.hibernate.reactive.provider.Settings;
import org.hibernate.reactive.provider.service.ReactiveGenerationTarget;
import org.hibernate.reactive.stage.Stage;
import org.hibernate.reactive.testing.SessionFactoryManager;
import org.hibernate.reactive.vertx.VertxInstance;
import org.hibernate.tool.schema.spi.SchemaManagementTool;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.runner.RunWith;

import java.util.concurrent.CompletionStage;
import io.smallrye.mutiny.Uni;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.Timeout;
import io.vertx.ext.unit.junit.VertxUnitRunner;

import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType;
import static org.hibernate.reactive.util.impl.CompletionStages.loop;

/**
* Base class for unit tests that need a connection to the selected db and
Expand All @@ -53,20 +64,31 @@
@RunWith(VertxUnitRunner.class)
public abstract class BaseReactiveTest {

public static SessionFactoryManager factoryManager = new SessionFactoryManager();

@Rule
public Timeout rule = Timeout.seconds( 5 * 60 );

@Rule
public RunTestOnContext vertxContextRule = new RunTestOnContext();
@ClassRule
public static RunTestOnContext vertxContextRule = new RunTestOnContext( () -> {
VertxOptions options = new VertxOptions();
options.setBlockedThreadCheckInterval( 5 );
options.setBlockedThreadCheckIntervalUnit( TimeUnit.MINUTES );
Vertx vertx = Vertx.vertx( options );
return vertx;
} );

private AutoCloseable session;
private AutoCloseable session, statelessSession;
private ReactiveConnection connection;
private org.hibernate.SessionFactory sessionFactory;
private ReactiveConnectionPool poolProvider;

protected static void test(TestContext context, CompletionStage<?> work) {
// this will be added to TestContext in the next vert.x release
Async async = context.async();
test( context.async(), context, work );
}

/**
* For when we need to create the {@link Async} in advance
*/
protected static void test(Async async, TestContext context, CompletionStage<?> work) {
work.whenComplete( (res, err) -> {
if ( res instanceof Stage.Session ) {
Stage.Session s = (Stage.Session) res;
Expand All @@ -84,7 +106,13 @@ protected static void test(TestContext context, CompletionStage<?> work) {
}

protected static void test(TestContext context, Uni<?> uni) {
Async async = context.async();
test( context.async(), context, uni );
}

/**
* For when we need to create the {@link Async} in advance
*/
protected static void test(Async async, TestContext context, Uni<?> uni) {
uni.subscribe().with(
res -> {
if ( res instanceof Mutiny.Session) {
Expand Down Expand Up @@ -116,33 +144,66 @@ protected Configuration constructConfiguration() {
return configuration;
}

public CompletionStage<Void> deleteEntities(Class<?>... entities) {
return deleteEntities( Arrays.stream( entities )
.map( BaseReactiveTest::defaultEntityName )
.collect( Collectors.toList() )
.toArray( new String[entities.length] ) );
}

private static String defaultEntityName(Class<?> aClass) {
int index = aClass.getName().lastIndexOf( '.' );
index = index > -1 ? index + 1 : 0;
return aClass.getName().substring( index );
}

public CompletionStage<Void> deleteEntities(String... entities) {
return getSessionFactory()
.withTransaction( (s, tx) -> loop( entities, name -> s
.createQuery( "from " + name ).getResultList()
.thenCompose( list -> s.remove( list.toArray( new Object[list.size()] ) ) ) ) );
}

@Before
public void before(TestContext context) {
Async async = context.async();
vertxContextRule.vertx()
.executeBlocking(
// schema generation is a blocking operation and so it causes an
// exception when run on the Vert.x event loop. So call it using
// Vertx.executeBlocking()
this::startFactoryManager,
event -> {
if ( event.succeeded() ) {
async.complete();
}
else {
context.fail( event.cause() );
}
}
);
}

private void startFactoryManager(Promise<Object> p) {
try {
factoryManager.start( () -> createHibernateSessionFactory() );
p.complete();
}
catch (Throwable e) {
p.fail( e );
}
}

private SessionFactory createHibernateSessionFactory() {
Configuration configuration = constructConfiguration();
StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder()
.addService( VertxInstance.class, (VertxInstance) () -> vertxContextRule.vertx() )
.applySettings( configuration.getProperties() );
addServices( builder );
StandardServiceRegistry registry = builder.build();
configureServices( registry );

// schema generation is a blocking operation and so it causes an
// exception when run on the Vert.x event loop. So call it using
// Vertx.executeBlocking()
Async async = context.async();
vertxContextRule.vertx().<SessionFactory>executeBlocking(
p -> p.complete( configuration.buildSessionFactory( registry ) ),
r -> {
if ( r.failed() ) {
context.fail( r.cause() );
}
else {
sessionFactory = r.result();
poolProvider = registry.getService( ReactiveConnectionPool.class );
async.complete();
}
}
);
SessionFactory sessionFactory = configuration.buildSessionFactory( registry );
return sessionFactory;
}

protected void addServices(StandardServiceRegistryBuilder builder) {}
Expand Down Expand Up @@ -179,6 +240,11 @@ public void after(TestContext context) {
session.close();
session = null;
}
if ( statelessSession != null && statelessSession.isOpen() ) {
statelessSession.close();
statelessSession = null;
}

if ( connection != null ) {
try {
connection.close();
Expand All @@ -188,14 +254,15 @@ public void after(TestContext context) {
connection = null;
}
}
}

if ( sessionFactory != null ) {
sessionFactory.close();
}
@AfterClass
public static void closeFactory() {
factoryManager.stop();
}

protected Stage.SessionFactory getSessionFactory() {
return sessionFactory.unwrap( Stage.SessionFactory.class );
return factoryManager.getHibernateSessionFactory().unwrap( Stage.SessionFactory.class );
}

/**
Expand All @@ -212,8 +279,17 @@ protected Stage.Session openSession() {
return newSession;
}

protected Stage.StatelessSession openStatelessSession() {
if ( statelessSession != null && statelessSession.isOpen() ) {
statelessSession.close();
}
Stage.StatelessSession newSession = getSessionFactory().openStatelessSession();
this.statelessSession = newSession;
return newSession;
}

protected CompletionStage<ReactiveConnection> connection() {
return poolProvider.getConnection().thenApply( c -> connection = c );
return factoryManager.getReactiveConnectionPool().getConnection().thenApply( c -> connection = c );
}

/**
Expand All @@ -230,8 +306,16 @@ protected Mutiny.Session openMutinySession() {
return newSession;
}

protected Mutiny.SessionFactory getMutinySessionFactory() {
return sessionFactory.unwrap( Mutiny.SessionFactory.class );
protected Mutiny.StatelessSession openMutinyStatelessSession() {
if ( statelessSession != null ) {
statelessSession.close();
}
Mutiny.StatelessSession newSession = getMutinySessionFactory().openStatelessSession();
this.statelessSession = newSession;
return newSession;
}

protected Mutiny.SessionFactory getMutinySessionFactory() {
return factoryManager.getHibernateSessionFactory().unwrap( Mutiny.SessionFactory.class );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.hibernate.LockMode;
import org.hibernate.annotations.BatchSize;
import org.hibernate.cfg.Configuration;

import org.junit.After;
import org.junit.Test;

import javax.persistence.CascadeType;
Expand Down Expand Up @@ -45,6 +47,13 @@ protected Configuration constructConfiguration() {
return configuration;
}

@After
public void cleanDb(TestContext context) {
test( context, getSessionFactory()
.withTransaction( (s, t) -> s.createQuery( "delete from Element" ).executeUpdate()
.thenCompose( v -> s.createQuery( "delete from Node" ).executeUpdate() ) ) );
}

@Test
public void testQuery(TestContext context) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.cfg.Configuration;
import org.hibernate.reactive.pool.ReactiveConnection;

import org.junit.After;
import org.junit.Test;

import io.vertx.ext.unit.TestContext;
Expand All @@ -24,6 +25,11 @@
public class BatchQueryOnConnectionTest extends BaseReactiveTest {
private static final int BATCH_SIZE = 20;

@After
public void cleanDb(TestContext context) {
test( context, deleteEntities( "DataPoint" ) );
}

@Test
public void testBatchInsertSizeEqMultiple(TestContext context) {
final List<List<Object[]>> paramsBatches = doBatchInserts( context, 50, BATCH_SIZE );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.hibernate.annotations.Cache;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

import org.junit.After;
import org.junit.Test;

import javax.persistence.Cacheable;
Expand All @@ -32,6 +34,11 @@ protected Configuration constructConfiguration() {
return configuration;
}

@After
public void cleanDB(TestContext context) {
getSessionFactory().close();
}

@Test
public void testCacheWithHQL(TestContext context) {
org.hibernate.Cache cache = getSessionFactory().getCache();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@
import java.util.concurrent.CompletionStage;
import java.util.stream.IntStream;


import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static org.hibernate.reactive.util.impl.CompletionStages.*;
import static org.hibernate.reactive.util.impl.CompletionStages.total;
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
import static org.hibernate.reactive.util.impl.CompletionStages.loopWithoutTrampoline;

import org.junit.Test;
import org.junit.runner.RunWith;

import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.reactive.util.impl.CompletionStages.completedFuture;
import static org.hibernate.reactive.util.impl.CompletionStages.loop;
import static org.hibernate.reactive.util.impl.CompletionStages.loopWithoutTrampoline;
import static org.hibernate.reactive.util.impl.CompletionStages.total;
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;

/**
* Tests the utility methods in {@link org.hibernate.reactive.util.impl.CompletionStages}
Expand Down
Loading