/*
* Copyright (c) 2014 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.werval.modules.jpa;
import io.werval.runtime.routes.RoutesParserProvider;
import io.werval.test.WervalHttpRule;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import io.werval.modules.jdbc.JDBC;
import static com.jayway.restassured.RestAssured.expect;
import static io.werval.api.mime.MimeTypes.APPLICATION_JSON;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;
/**
* JPA Plugin Test.
*/
public class JPAPluginTest
{
@ClassRule
public static final WervalHttpRule WERVAL = new WervalHttpRule( new RoutesParserProvider(
"GET /directUsage io.werval.modules.jpa.Controller.directUsage\n"
+ "GET /withTransaction io.werval.modules.jpa.Controller.withTransaction\n"
+ "GET /transactional io.werval.modules.jpa.Controller.transactional\n"
+ "GET /multithreadedDirectUsage io.werval.modules.jpa.Controller.multithreadedDirectUsage\n"
+ "GET /multithreadedWithTransaction io.werval.modules.jpa.Controller.multithreadedWithTransaction\n"
+ "GET /multithreadedTransactional io.werval.modules.jpa.Controller.multithreadedTransactional\n"
+ "GET /metrics io.werval.modules.metrics.Tools.metrics\n"
) );
@BeforeClass
public static void setupDatabaseSchemas()
throws SQLException
{
// Create schema using plain JDBC, you'll want to use something like Liquibase or Flyway in a real application
JDBC jdbc = WERVAL.application().plugin( JDBC.class );
// Default PU
String createFooTable = "CREATE TABLE FOOENTITY (ID bigint AUTO_INCREMENT, NAME varchar(255), PRIMARY KEY (ID));";
try( Connection connection = jdbc.connection();
PreparedStatement statement = connection.prepareStatement( createFooTable ) )
{
statement.execute();
}
// Another PU
String createBarTable = "CREATE TABLE BARENTITY (ID bigint AUTO_INCREMENT, NAME varchar(255), PRIMARY KEY (ID));";
try( Connection connection = jdbc.connection( "another" );
PreparedStatement statement = connection.prepareStatement( createBarTable ) )
{
statement.execute();
}
}
@AfterClass
public static void assertMetrics()
{
expect()
.statusCode( 200 )
.contentType( APPLICATION_JSON )
.body( "histograms.'com.eclipse.persistence.histograms.CacheSizeBarEntity'.count", is( 3 ) )
.body( "histograms.'com.eclipse.persistence.histograms.CacheSizeFooEntity'.count", is( 4 ) )
.body( "timers.'com.eclipse.persistence.timers.InsertObjectQuery'.count", is( 7 ) )
.body( "meters.'com.eclipse.persistence.meters.ClientSessionCreates'.count", is( 8 ) )
.body( "meters.'com.eclipse.persistence.meters.ClientSessionReleases'.count", is( 8 ) )
.body( "meters.'com.eclipse.persistence.meters.CacheHits'.count", is( 6 ) )
.body( "meters.'com.eclipse.persistence.meters.ConnectCalls'.count", is( 4 ) )
.body( "meters.'com.eclipse.persistence.meters.InsertObjectQuery'.count", is( 7 ) )
.body( "meters.'com.eclipse.persistence.meters.InsertObjectQuery:io.werval.modules.jpa.BarEntity'.count", is( 3 ) )
.body( "meters.'com.eclipse.persistence.meters.InsertObjectQuery:io.werval.modules.jpa.FooEntity'.count", is( 4 ) )
.body( "meters.'com.eclipse.persistence.meters.ReadObjectQuery'.count", is( 6 ) )
.body( "meters.'com.eclipse.persistence.meters.ReadObjectQuery:io.werval.modules.jpa.BarEntity:readBarEntity'.count", is( 3 ) )
.body( "meters.'com.eclipse.persistence.meters.ReadObjectQuery:io.werval.modules.jpa.BarEntity:readBarEntity:CacheHits'.count", is( 3 ) )
.body( "meters.'com.eclipse.persistence.meters.ReadObjectQuery:io.werval.modules.jpa.FooEntity:readFooEntity'.count", is( 3 ) )
.body( "meters.'com.eclipse.persistence.meters.ReadObjectQuery:io.werval.modules.jpa.FooEntity:readFooEntity:CacheHits'.count", is( 3 ) )
.body( "meters.'com.eclipse.persistence.meters.UnitOfWorkCreates'.count", is( 8 ) )
.body( "meters.'com.eclipse.persistence.meters.UnitOfWorkReleases'.count", is( 8 ) )
.body( "meters.'com.eclipse.persistence.meters.UnitOfWorkCommits'.count", is( 10 ) )
.body( "meters.'com.eclipse.persistence.meters.ValueReadQuery'.count", is( 7 ) )
.body( "meters.'com.eclipse.persistence.meters.ValueReadQuery:SEQ_GEN_IDENTITY'.count", is( 7 ) )
.when()
.get( "/metrics" );
}
@Test
public void multiplePersistenceUnits()
{
JPA jpa = WERVAL.application().plugin( JPA.class );
assertThat( jpa.emf(), equalTo( jpa.emf( "default" ) ) );
assertThat( jpa.emf( "another" ), notNullValue() );
try
{
jpa.emf( "do not exists" );
}
catch( PersistenceException expected )
{
}
}
@Test
public void outOfContextDirectUsage()
{
// Use JPA
JPA jpa = WERVAL.application().plugin( JPA.class );
EntityManager em = jpa.newEntityManager();
try
{
em.getTransaction().begin();
FooEntity foo = new FooEntity( "FOO" );
em.persist( foo );
em.getTransaction().commit();
Long id = foo.getId();
em.getTransaction().begin();
foo = em.find( FooEntity.class, id );
assertThat( foo.getName(), equalTo( "FOO" ) );
em.getTransaction().commit();
}
finally
{
em.close();
}
}
@Test
public void outOfContextWithTransaction()
{
// Use JPA.withTransaction
JPA jpa = WERVAL.application().plugin( JPA.class );
try
{
Long id = jpa.supplyWithReadWriteTx(
"another",
(em) ->
{
System.out.println( "WILL PERSIST" );
BarEntity bar = new BarEntity( "BAR" );
em.persist( bar );
em.flush();
System.out.println( "HAS PERSISTED: " + bar.getId() );
return bar.getId();
}
);
String name = jpa.supplyWithReadOnlyTx(
"another",
(em) -> em.find( BarEntity.class, id ).getName()
);
assertThat( name, equalTo( "BAR" ) );
}
finally
{
jpa.em( "another" ).close();
}
}
@Test
public void inContextDirectUsage()
{
expect()
.statusCode( 200 )
.body( containsString( "FOO" ) )
.when()
.get( "/directUsage" );
}
@Test
public void inContextWithTransaction()
{
expect()
.statusCode( 200 )
.body( containsString( "BAR" ) )
.when()
.get( "/withTransaction" );
}
@Test
public void inContextTransactional()
{
expect()
.statusCode( 200 )
.body( containsString( "FOO" ) )
.when()
.get( "/transactional" );
}
@Test
public void multithreadedContextDirectUsage()
{
expect()
.statusCode( 200 )
.body( containsString( "FOO" ) )
.when()
.get( "/multithreadedDirectUsage" );
}
@Test
public void multithreadedContextWithTransaction()
{
expect()
.statusCode( 200 )
.body( containsString( "BAR" ) )
.when()
.get( "/multithreadedWithTransaction" );
}
@Test
public void multithreadedContextTransactional()
{
expect()
.statusCode( 200 )
.body( containsString( "FOO" ) )
.when()
.get( "/multithreadedTransactional" );
}
}