In this post I’m about to figure out how to use the following things in Java project:
- How to leverage in-memory H2 database? Particularly configuring it in maven pom.xml
- How to to initialize and use H2 database via Hibernate?
- How to use JNDI naming for DB connectivity on Jetty?
As a base for sample project I’ll take a tiny webservice application from post How to use Jetty to run Java web application locally? It already has configured a Jetty via Maven plugin.
We’ll extend project by adding REST resource which counts visits and returns a total count.
This is a Maven project, thus we manage dependencies, embed and configure Jetty via pom.xml. Lets start implementing those 3 requirements mentioned above.
- Add H2 in-memory database
This is the firs and the easiest step. It is enough just to add maven dependency and database is there. As we run this project using Jetty, need to put following dependency into Jetty plugin scope. That will become important when we configure JNDI datasource to access database.
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.3.176</version> </dependency>
If you do not add this dependency in Jetty plugin scope, will get a following exception when starting Jetty:
java.lang.ClassNotFoundException: org.h2.jdbcx.JdbcDataSource
- Attach Hibernate and configure it for H2 database
- Create hibernate.cfg.xml in resource classpath:
<?xml version = "1.0" encoding = "utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "//www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.H2Dialect </property> <property name="hibernate.connection.datasource">jdbc/h2test</property> <property name="hibernate.connection.driver_class"> org.h2.Driver </property> <property name="hibernate.connection.url"> jdbc:h2:mem:test2 </property> <property name="hibernate.connection.username"> sa </property> <property name="hibernate.connection.password"> sa </property> <property name="hibernate.hbm2ddl.auto"> update </property> <property name="hibernate.show_sql"> true </property> <mapping class="com.programmersnotes.VisitorEntity"/> </session-factory> </hibernate-configuration>
Preceding hibernate configuration takes care of table creation in DB if it not yet exists. As we’re tackling in-memory DB, it is always fresh, so each time application starts, hibernate creates necessary structures. Here is our hibernate entity definition, which is used to generate DDL and DML scripts:
@Entity @Table(name = "Visitors", uniqueConstraints = { @UniqueConstraint(columnNames = "ID"), }) public class VisitorEntity { @Id @Column(name = "ID", unique = true, nullable = false) private String visitorUUID; @Column(name = "VISITING_TIME") private String visitingTime; public VisitorEntity() { } public VisitorEntity(String visitorUUID, String visitingTime) { this.visitorUUID = visitorUUID; this.visitingTime = visitingTime; } public String getVisitorUUID() { return visitorUUID; } public void setVisitorUUID(String visitorUUID) { this.visitorUUID = visitorUUID; } public String getVisitingTime() { return visitingTime; } public void setVisitingTime(String visitingTime) { this.visitingTime = visitingTime; } }
- Create a new REST resource controller for “/visit” endpoint, map getVisitCount() method and add logic to insert visitor record into DB and select total count afterwards. This is a very simplified and minimized code snipped, just to get understanding how it works:
@Path("/visit") @GET public String getVisit() throws Exception { Session session = new Configuration().configure().buildSessionFactory().openSession(); session.beginTransaction(); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDateTime now = LocalDateTime.now(); VisitorEntity visitor = new VisitorEntity(UUID.randomUUID().toString(), dtf.format(now)); session.save(visitor); session.getTransaction().commit(); Query query = session.createQuery("select count(*) from VisitorEntity"); Long count = (Long) query.uniqueResult(); session.close(); return "Total visits: " + count; }
Example we’ve created until now can already do its job. The last step will only replace direct DB connection configuration in hibernate.cfg.xml with JNDI resource name.
- Switch direct DB connection configuration to JNDI datasource. For this we need to modify pom.xml, hibernate.cfg.xml and add extra configuration file jetty-jndi-config.xml. The latter is basically for extracting connection details to be leveraged by JNDI datasource.
- Add couple more dependencies to enable Jetty JNDI support:
<dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-plus</artifactId> <version>9.2.29.v20191105</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-jndi</artifactId> <version>9.2.29.v20191105</version> </dependency>
-
- In hibernate.cfg.xml remove connection properties and add JNDI datasource name:
<?xml version = "1.0" encoding = "utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "//www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.H2Dialect </property> <property name="hibernate.connection.datasource">jdbc/h2test</property> <property name="hibernate.hbm2ddl.auto"> update </property> <property name="hibernate.show_sql"> true </property> <mapping class="com.programmersnotes.VisitorEntity"/> </session-factory> </hibernate-configuration>
-
- Add extra Jetty configuration in pom.xml:
<configuration> <jettyXml>jetty-jndi-config.xml</jettyXml> </configuration>
-
- Create file jetty-jndi-config.xml next to pom.xml in filesystem:
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <New id="datasource1" class="org.eclipse.jetty.plus.jndi.Resource"> <Arg>jdbc/h2test</Arg> <Arg> <New class="org.h2.jdbcx.JdbcDataSource"> <Set name="url">jdbc:h2:mem:test2</Set> <Set name="user">sa</Set> </New> </Arg> </New> </Configure>
Here you see JDBC datasource driver class org.h2.jdbcx.JdbcDataSource. It is part of “h2” dependency. As mentioned earlier in this post – ClassNotFoundException is raised if the latter dependency is not included in Jetty plugin scope. Although if there is no JNDI datasource used, dependency has to be in the general Maven dependency list in order Hibernate to see it.
Thats it, we have just covered all three items and learned how things are done. Demo application can be downloaded here. Endpoint URI to trigger test: //localhost:9999/rest/h2/visit.