Here's how to do this yourself. This is using Maven 3.5.0 or later. I presume that you're not a Maven or Java novice.
First, let's look at the pom.xml
.
You'll need to import the Helidon BOM, so in your
<dependencyManagement>
stanza you'll need:
<dependency>
<groupId>io.helidon</groupId>
<artifactId>helidon-bom</artifactId>
<version>1.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Then, in your actual <dependencies>
section, you'll need to bring in
Helidon MicroProfile, obviously:
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>helidon-microprofile-2.2</artifactId>
</dependency>
(That will get its version from the BOM, which is 1.2.1
as you can
see.)
Now you'll also need provided
-scoped dependencies on the two APIs
you'll be working with directly. I like to specify versions in my
<dependencyManagement>
section, like this:
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.2</version>
</dependency>
...and then in my <dependencies>
stanza simply "point" to them, like so:
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<scope>provided</scope>
</dependency>
So that gets you the APIs you'll program against. Now you need some implementations that will actually be there at runtime.
For the JTA stuff, you'll want to have this in
<dependencies>
:
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-jta-weld</artifactId>
<scope>runtime</scope>
</dependency>
Note that these are in runtime
scope, i.e. you don't compile against
them; they're just drop-in components.
All right, now let's start building up JPA support. The first thing
we'll need is a connection pool, so let's use Helidon's. In
<dependencies>
you should do this:
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-datasource-hikaricp</artifactId>
<scope>runtime</scope>
</dependency>
Next, we need a component that will bring in a JPA provider and will
adapt that provider to how it will be used. Put this in your
<dependencies>
stanza:
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-eclipselink-cdi</artifactId>
<scope>runtime</scope>
</dependency>
Another runtime
component.
A database for testing would be handy, so put this in your <dependencyManagement>
stanza:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
...and this in your <dependencies>
stanza:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
Now, finally, we need the CDI glue to stitch this together. Put this
in your <dependencies>
stanza:
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-jpa</artifactId>
<scope>runtime</scope>
</dependency>
Then there are some goodies that really are optional. Woodstox will
make XML parsing much faster, so it's nice to put this in your
<dependencyManagement>
stanza:
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>5.2.0</version>
</dependency>
...and this in your <dependencies>
stanza:
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Jandex will make annotation scanning super fast so do this in your
<dependencyManagement>
stanza:
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>2.1.1.Final</version>
</dependency>
...and this in your <dependencies>
stanza:
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<scope>runtime</scope>
</dependency>
When you put all of this together, you end up with something like this (obviously sanity-check the versions if you like):
<dependencyManagement>
<dependencies>
<!-- Imports. -->
<dependency>
<groupId>io.helidon</groupId>
<artifactId>helidon-bom</artifactId>
<version>1.2.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Normal dependencies. -->
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Test-scoped dependencies. -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- Runtime-scoped dependencies. -->
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-datasource-hikaricp</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-eclipselink</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-jpa</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.helidon.integrations.cdi</groupId>
<artifactId>helidon-integrations-cdi-jta-weld</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Provided-scoped dependencies. -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Compile-scoped dependencies. -->
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>helidon-microprofile-2.2</artifactId>
</dependency>
</dependencies>
OK that was fun. Obviously using Maven tricks you can bundle all this up in various ways (one of which I blog about here).
On to the actual development.
The support I've added to Helidon MicroProfile looks as much like Java EE as possible, while using CDI and only CDI as the backplane.
Since we're using CDI, create a
src/main/resources/META-INF/beans.xml
file and make it look exactly
like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
version="2.0"
bean-discovery-mode="annotated">
</beans>
Also right now before you forget create
src/test/resources/META-INF/beans.xml
and make it look identical:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
version="2.0"
bean-discovery-mode="annotated">
</beans>
OK, we're set up for CDI success.
So step one in actual development is to write a JAX-RS application.
To do this portably, you'll need a root resource class and an
Application
to state authoritatively that it owns it.
The root resource class might be named HelloWorldResource
and might
look like this to start (note that example code is subject to this
project's license unless it explicitly says otherwise):
package io.github.ljnelson.helidon.mp.jpa;
import java.util.Objects;
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceException; // for javadoc only
import javax.persistence.SynchronizationType;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("")
@RequestScoped
public class HelloWorldResource {
@PersistenceContext(unitName = "test")
private EntityManager entityManager;
public HelloWorldResource() {
super();
}
@GET
@Path("{firstPart}")
@Produces(MediaType.TEXT_PLAIN)
public String get(@PathParam("firstPart") final String firstPart) {
return "world";
}
}
Here we're not using it yet, but you can see that we've asked for an
EntityManager
to be handed to us (note the @PersistenceContext
annotation).
This EntityManager
will behave exactly as an EntityManager
would
in a fat application server, i.e. not like an EntityManager you
create by hand. Specifically, if there is a transaction in effect, it
will automatically join it and do all the other Java EE-like things
you're probably used to if you've programmed Java EE JPA stuff in the
past.
The Application
might look like this:
package io.github.ljnelson.helidon.mp.jpa;
import java.util.Collections;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.core.Application;
@ApplicationScoped
public class GreetingApplication extends Application {
public GreetingApplication() {
super();
}
@Override
public Set<Class<?>> getClasses() {
return Collections.singleton(HelloWorldResource.class);
}
}
Let's beef up the get()
method in our root resource class. Let's
change it to use the EntityManager
:
@GET
@Path("{firstPart}")
@Produces(MediaType.TEXT_PLAIN)
public String get(@PathParam("firstPart") final String firstPart) {
Objects.requireNonNull(firstPart);
assert this.entityManager != null;
final TypedQuery<Greeting> query = this.entityManager.createNamedQuery("findByFirstPart", Greeting.class);
query.setParameter("firstPart", firstPart);
return query.getSingleResult().toString();
}
Note in particular there's no starting of transactions or other boilerplate; that's all handled for you. In this case, no transaction is in effect.
Obviously for that bit of code to work we'll need an entity called
Greeting
, which might look like this:
package io.github.ljnelson.helidon.mp.jpa;
import java.util.Objects;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Access(AccessType.FIELD)
@Entity(name = "Greeting")
@NamedQuery(name = "findByFirstPart",
query = "SELECT g FROM Greeting g WHERE g.firstPart = :firstPart")
@Table(name = "GREETING",
uniqueConstraints = {
@UniqueConstraint(columnNames = {
"FIRSTPART", "SECONDPART"
},
name = "UNQ_GREETING"
)
})
public class Greeting {
@Id
@Column(name = "ID", insertable = true, nullable = false, updatable = false)
@GeneratedValue
private Long id;
@Basic(optional = false)
@Column(name = "FIRSTPART", insertable = true, nullable = false, updatable = true)
private String firstPart;
@Basic(optional = false)
@Column(name = "SECONDPART", insertable = true, nullable = false, updatable = true)
private String secondPart;
/**
* Creates a new {@link Greeting}; required by the JPA specification
* and for no other purpose.
*
* @deprecated Please use the {@link #Greeting(Long, String,
* String)} constructor instead.
*
* @see #Greeting(Long, String, String)
*/
@Deprecated
protected Greeting() {
super();
}
/**
* Creates a new {@link Greeting}.
*
* @param id the identifier; may be {@code null}
*
* @param firstPart the first part of the greeting; must not be
* {@code null}
*
* @param secondPart the second part of the greeting; must not be
* {@code null}
*
* @exception NullPointerException if {@code firstPart} or {@code
* secondPart} is {@code null}
*/
public Greeting(final Long id, final String firstPart, final String secondPart) {
super();
this.id = id;
this.firstPart = Objects.requireNonNull(firstPart);
this.secondPart = Objects.requireNonNull(secondPart);
}
/**
* Returns the identifier of this {@link Greeting}.
*
* <p>This method may return {@code null}.</p>
*
* @return the identifier of this {@link Greeting} or {@code null}
*/
public Long getId() {
return this.id;
}
/**
* Returns a {@link String} representation of the second part of
* this {@link Greeting}.
*
* <p>This method never returns {@code null}.</p>
*
* @return a non-{@code null} {@link String} representation of the
* second part of this {@link Greeting}
*/
@Override
public String toString() {
return this.secondPart;
}
}
OK, so that's all the code you need for a fetch-type operation. Now let's set up the test database and the connection pool.
The connection pool creates itself from configuration properties, and
the kinds of configuration properties in play when you're talking
about Helidon MicroProfile are MicroProfile Config configuration
properties, so to get started let's add them to the default place
where such properties live. MicroProfile Config is very flexible so
there are a zillion other places you could put them, but this is easy.
Create src/test/resources/META-INF/microprofile-config.properties
and make it look like this:
javax.sql.DataSource.test.dataSourceClassName=org.h2.jdbcx.JdbcDataSource
javax.sql.DataSource.test.dataSource.url=jdbc:h2:mem:test;INIT=CREATE TABLE GREETING (ID BIGINT NOT NULL, FIRSTPART VARCHAR NOT NULL, SECONDPART VARCHAR NOT NULL, PRIMARY KEY (ID))\\;ALTER TABLE GREETING ADD CONSTRAINT UNQ_GREETING UNIQUE (FIRSTPART, SECONDPART)\\;CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT NUMERIC(38), PRIMARY KEY (SEQ_NAME))\\;INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) VALUES ('SEQ_GEN', 0)\\;INSERT INTO GREETING (ID, FIRSTPART, SECONDPART) VALUES (-1, 'hello', 'world')
javax.sql.DataSource.test.username=sa
javax.sql.DataSource.test.password=
Anything starting with javax.sql.DataSource.
is a piece of
information destined for the guts of the Helidon MicroProfile
connection pool, which is documented over
here.
Here you can see that we're setting up the DataSource
to be backed
by an in-memory H2 database named test
. It's using the convenient
H2 feature of being able to run some initialization SQL on
startup.
The user is the default sa
user, and the password is the empty
string.
Next, we'll need to tell JPA what we're up to. Create
src/test/resources/META-INF/persistence.xml
and make it look like
this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit
name="test"
transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>io.github.ljnelson.helidon.mp.jpa.Greeting</class>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="sql-script"/>
<property name="eclipselink.create-ddl-jdbc-file-name" value="createDDL_ddlGeneration.jdbc"/>
<property name="eclipselink.deploy-on-startup" value="true"/>
<property name="eclipselink.jdbc.native-sql" value="true"/>
<property name="eclipselink.logging.logger" value="JavaLogger"/>
<property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.H2Platform"/>
<property name="eclipselink.target-server" value="org.microbean.eclipselink.cdi.CDISEPlatform"/>
</properties>
</persistence-unit>
</persistence>
Here we've defined a persistence unit named test
. You'll note
that's what our DataSource
was named as well. That's on purpose.
(Oh, hey, did you see that little transaction-type="JTA"
nugget in
there? That's interesting, isn't it?)
This persistence unit uses Eclipselink as the provider.
There are various Eclipselink-specific
properties
here, most of which just make things nicer. One that is definitely
not optional in this case is the eclipselink.target-server
property,
which must bet set to a value of
org.microbean.eclipselink.cdi.CDISEPlatform
.
This allows Eclipselink to think that it's running in a Java EE server
on occasion but helpfully tells it to stop trying to use JNDI.
(There's an analogous Hibernate
construct
too.)
So now we have:
- A JAX-RS application
- A JAX-RS implementation (Helidon MicroProfile)
- A connection pool (Helidon's HikariCP integration)
- A JPA implementation (EclipseLink)
- A transaction manager (Narayana)
- Various runtime components that stitch it all together
- Configuration that sets up the database and links it to the persistence unit
We can add a JUnit test that tries to drive all this. It might look like this:
package io.github.ljnelson.helidon.mp.jpa;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.spi.CDI;
import io.helidon.microprofile.server.Server;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class TestJPAIntegration {
private Server server;
private URL baseUrl;
public TestJPAIntegration() {
super();
}
@Before
public void startServer() throws MalformedURLException {
this.server = Server.create().start();
this.baseUrl = new URL("http://127.0.0.1:" + server.port());
}
@After
public void stopServer() {
this.server.stop();
this.baseUrl = null;
}
@Test
public void testGet() throws Exception {
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(this.baseUrl, "/hello").openStream(), "UTF-8"))) {
assertEquals("world", reader.readLine());
}
}
}
If you run mvn test
at this point (I haven't tried it, but you'll
note an uncanny resemblance between this README and this actual
project, which works!), you should see the test pass.
Now let's go back to the root resource class and add a stupid POST
method:
@POST
@Path("{firstPart}")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
@Transactional(TxType.REQUIRED)
public String post(@PathParam("firstPart") final String firstPart,
final String secondPart) {
Objects.requireNonNull(firstPart);
Objects.requireNonNull(secondPart);
assert this.entityManager != null;
Greeting greeting = new Greeting(null, firstPart, secondPart);
greeting = this.entityManager.merge(greeting);
return String.valueOf(greeting.getId());
}
Note the
@Transactional
(TxType.REQUIRED)
annotation.
Normally putting this kind of logic directly on a JAX-RS class would be quite rightly frowned upon but we're in Happy Example Land™ so we get to do things like this.
This annotation replaces what EJBs used to do for us. Now we can mark methods as requiring EJB transaction semantics, only without all the other EJB stuff no one actually cared about. Here, we're saying: "Please start a JTA transaction encompassing this method".
Remember our friend the EntityManager
? That EntityManager
will
automatically take part in that transaction, just as if you were in a
big, fat Java EE application server. Only you're not.
We can update our test to test this behavior too:
@Test
public void testPost() throws Exception {
final HttpURLConnection c = (HttpURLConnection)new URL(this.baseUrl, "/hello").openConnection();
assertNotNull(c);
c.setDoOutput(true);
c.setRequestProperty("Content-Type", "text/plain");
c.setRequestMethod("POST");
final byte[] helloBytes = "hello".getBytes("UTF8");
assertNotNull(helloBytes);
c.setRequestProperty("Content-Length", String.valueOf(helloBytes.length));
try(final OutputStream stream = new BufferedOutputStream(c.getOutputStream())) {
assertNotNull(stream);
stream.write(helloBytes, 0, helloBytes.length);
}
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (final InputStream inputStream = new BufferedInputStream(c.getInputStream())) {
assertNotNull(inputStream);
int bytesRead;
final byte[] bytes = new byte[4096];
while ((bytesRead = inputStream.read(bytes, 0, bytes.length)) != -1) {
baos.write(bytes, 0, bytesRead);
}
}
assertEquals("1", baos.toString("UTF8"));
}
That is some convoluted JDK gymnastics that sends a POST
request to
our REST endpoint. If you run mvn test
at this point, everything
should still pass, and if you could see what was going on you'd see
SQL going into the database.
To see what's going on, it might help to set up Java logging
appropriately. Let's create src/test/logging.properties
and make it
look like this:
.level=WARNING
org.eclipse.persistence.level=FINE
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level=FINEST
Then let's tell Maven's Surefire, the testing plugin, to make sure
it's passed when it forks off a JVM to run our test. In your
<plugins>
stanza you'll want something like this:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<java.util.logging.config.file>${basedir}/src/test/logging.properties</java.util.logging.config.file>
</systemPropertyVariables>
</configuration>
</plugin>
Now when you run mvn test
you should see output kind of like this:
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.github.ljnelson.helidon.mp.jpa.TestJPAIntegration
Aug 28, 2019 12:26:20 PM com.arjuna.ats.jta.common.JTAEnvironmentBean setXaRecoveryNodes
DEBUG: Setting up node identifiers '[1]' for which recovery will be performed
Aug 28, 2019 12:26:22 PM io.helidon.microprofile.security.SecurityMpService configure
INFO: Security extension for microprofile is enabled, yet security configuration is missing from config (requires providers configuration at key security.providers). Security will not have any valid provider.
Aug 28, 2019 12:26:22 PM io.smallrye.openapi.api.OpenApiDocument initialize
INFO: OpenAPI document initialized: io.smallrye.openapi.api.models.OpenAPIImpl@65f2f9b0
Aug 28, 2019 12:26:23 PM io.helidon.webserver.NettyWebServer <init>
INFO: Version: 1.2.1
Aug 28, 2019 12:26:23 PM io.helidon.webserver.NettyWebServer lambda$start$8
INFO: Channel '@default' started: [id: 0x3c0e6a63, L:/0:0:0:0:0:0:0:0:7001]
Aug 28, 2019 12:26:23 PM io.helidon.microprofile.server.ServerImpl lambda$start$10
INFO: Server started on http://localhost:7001 (and all other host addresses) in 103 milliseconds.
Aug 28, 2019 12:26:24 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.server
FINE: Configured server platform: io.helidon.integrations.cdi.eclipselink.CDISEPlatform
Aug 28, 2019 12:26:25 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test
INFO: EclipseLink, version: Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a
Aug 28, 2019 12:26:25 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test
INFO: Server: CDISEPlatform
Aug 28, 2019 12:26:25 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: connecting(DatabaseLogin(
platform=>H2Platform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
Aug 28, 2019 12:26:25 PM com.zaxxer.hikari.HikariDataSource <init>
INFO: HikariPool-1 - Starting...
Aug 28, 2019 12:26:25 PM com.zaxxer.hikari.HikariDataSource <init>
INFO: HikariPool-1 - Start completed.
Aug 28, 2019 12:26:25 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: Connected: jdbc:h2:mem:test
User: SA
Database: H2 Version: 1.4.199 (2019-03-13)
Driver: H2 JDBC Driver Version: 1.4.199 (2019-03-13)
Aug 28, 2019 12:26:25 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: connecting(DatabaseLogin(
platform=>H2Platform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
Aug 28, 2019 12:26:25 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: Connected: jdbc:h2:mem:test
User: SA
Database: H2 Version: 1.4.199 (2019-03-13)
Driver: H2 JDBC Driver Version: 1.4.199 (2019-03-13)
Aug 28, 2019 12:26:26 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
INFO: /file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test login successful
Aug 28, 2019 12:26:26 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.sql
FINE: SELECT ID, FIRSTPART, SECONDPART FROM GREETING WHERE (FIRSTPART = ?)
bind => [hello]
Aug 28, 2019 12:26:26 PM io.helidon.webserver.NettyWebServer lambda$start$6
INFO: Channel '@default' closed: [id: 0x3c0e6a63, L:/0:0:0:0:0:0:0:0:7001]
Aug 28, 2019 12:26:26 PM io.helidon.microprofile.server.ServerImpl lambda$stopWebServer$12
INFO: Server stopped in 23 milliseconds.
Aug 28, 2019 12:26:26 PM com.zaxxer.hikari.HikariDataSource close
INFO: HikariPool-1 - Shutdown initiated...
Aug 28, 2019 12:26:26 PM com.zaxxer.hikari.HikariDataSource close
INFO: HikariPool-1 - Shutdown completed.
Aug 28, 2019 12:26:26 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: disconnect
Aug 28, 2019 12:26:26 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
INFO: /file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test logout successful
Aug 28, 2019 12:26:27 PM io.helidon.microprofile.security.SecurityMpService configure
INFO: Security extension for microprofile is enabled, yet security configuration is missing from config (requires providers configuration at key security.providers). Security will not have any valid provider.
Aug 28, 2019 12:26:27 PM io.smallrye.openapi.api.OpenApiDocument initialize
INFO: OpenAPI document initialized: io.smallrye.openapi.api.models.OpenAPIImpl@594131f2
Aug 28, 2019 12:26:27 PM io.helidon.webserver.NettyWebServer <init>
INFO: Version: 1.2.1
Aug 28, 2019 12:26:27 PM io.helidon.webserver.NettyWebServer lambda$start$8
INFO: Channel '@default' started: [id: 0x569cf601, L:/0:0:0:0:0:0:0:0:7001]
Aug 28, 2019 12:26:27 PM io.helidon.microprofile.server.ServerImpl lambda$start$10
INFO: Server started on http://localhost:7001 (and all other host addresses) in 3 milliseconds.
Aug 28, 2019 12:26:27 PM com.arjuna.ats.arjuna.recovery.TransactionStatusManager addService
DEBUG: com.arjuna.ats.arjuna.recovery.ActionStatusService starting
Aug 28, 2019 12:26:27 PM com.arjuna.ats.internal.arjuna.recovery.TransactionStatusManagerItem <init>
DEBUG: TransactionStatusManagerItem host: {0} port: {1}
Aug 28, 2019 12:26:27 PM com.arjuna.ats.arjuna.recovery.TransactionStatusManager start
INFO: ARJUNA012170: TransactionStatusManager started on port 53066 and host 127.0.0.1 with service com.arjuna.ats.arjuna.recovery.ActionStatusService
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.server
FINE: Configured server platform: io.helidon.integrations.cdi.eclipselink.CDISEPlatform
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test
INFO: EclipseLink, version: Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test
INFO: Server: CDISEPlatform
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: connecting(DatabaseLogin(
platform=>H2Platform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
Aug 28, 2019 12:26:27 PM com.zaxxer.hikari.HikariDataSource <init>
INFO: HikariPool-2 - Starting...
Aug 28, 2019 12:26:27 PM com.zaxxer.hikari.HikariDataSource <init>
INFO: HikariPool-2 - Start completed.
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: Connected: jdbc:h2:mem:test
User: SA
Database: H2 Version: 1.4.199 (2019-03-13)
Driver: H2 JDBC Driver Version: 1.4.199 (2019-03-13)
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: connecting(DatabaseLogin(
platform=>H2Platform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: Connected: jdbc:h2:mem:test
User: SA
Database: H2 Version: 1.4.199 (2019-03-13)
Driver: H2 JDBC Driver Version: 1.4.199 (2019-03-13)
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
INFO: /file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test login successful
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.sql
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [50, SEQ_GEN]
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.sql
FINE: SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [SEQ_GEN]
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.sql
FINE: INSERT INTO GREETING (ID, FIRSTPART, SECONDPART) VALUES (?, ?, ?)
bind => [1, hello, hello]
Aug 28, 2019 12:26:27 PM io.helidon.webserver.NettyWebServer lambda$start$6
INFO: Channel '@default' closed: [id: 0x569cf601, L:/0:0:0:0:0:0:0:0:7001]
Aug 28, 2019 12:26:27 PM io.helidon.microprofile.server.ServerImpl lambda$stopWebServer$12
INFO: Server stopped in 2 milliseconds.
Aug 28, 2019 12:26:27 PM com.zaxxer.hikari.HikariDataSource close
INFO: HikariPool-2 - Shutdown initiated...
Aug 28, 2019 12:26:27 PM com.zaxxer.hikari.HikariDataSource close
INFO: HikariPool-2 - Shutdown completed.
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
CONFIG: disconnect
Aug 28, 2019 12:26:27 PM org.eclipse.persistence.session./file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test.connection
INFO: /file:/Users/LANELSON/Projects/github/ljnelson/helidon-mp-jpa/target/classes/_test logout successful
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.189 s - in io.github.ljnelson.helidon.mp.jpa.TestJPAIntegration
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17.236 s
[INFO] Finished at: 2019-08-28T12:26:28-07:00
[INFO] ------------------------------------------------------------------------
Yay!
- Narayana: A JTA transaction manager. Battle-tested. I helped with the CDI internals.
- Eclipselink: A JPA implementation.
- Weld: the CDI reference implementation.
- HikariCP: The fastest connection pool implementation.
- H2: A nice little Java database.
- Woodstox: Stax XML parser that flies.
- Jandex: Don't scan annotations by loading classes; look at efficient indices instead.