A beginner’s guide to the high-performance-java-persistence GitHub repository
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!
Introduction
When I started writing High-Performance Java Persistence, I realized I needed a GitHub repository to host all the test cases I needed for the code snippets in my book, and that’s how the high-performance-java-persistence GitHub repository was born.
The high-performance-java-persistence GitHub repository is a collection of integration tests and utilities so that you can test JDBC, JPA, Hibernate and jOOQ features with the utmost ease.
Collection of integration tests and utilities so that you can test JDBC, JPA, Hibernate and jOOQ features - @vlad_mihalcea https://t.co/FOCcW98MAs pic.twitter.com/2Xily00OIu
— Java (@java) February 2, 2018
Video
Because this repository is really important for working on my blog, book or video courses, I decided to record the following video and share it with you on YouTube:
This video is just one of the many awesome video lessons you can find in my High-Performance Java Persistence video course.
Integration Testing
When working with a database system and you want to test the data access layer code, unit tests don’t help you very much. Instead, you need integration tests that run on a database system that’s similar to the one you have in production.
That’s also how we are testing the Hibernate ORM project.
Now, in the high-performance-java-persistence GitHub repository, you are going to find lots of integration tests. As of writing, there are over 440 integration tests covering the most common scenarios you might bump into when using JDBC, JPA, or Hibernate.
Database schema management
While for a production system you really want to use Flyway to manage the database schema, when your goal is to create self-contained integration tests, the Hibernate hbm2ddl tool is an awesome choice.
So, you don’t need to write any database-specific DDL scripts for your integration tests, and that gives you a great advantage when switching from one relational database to another.
As long as you extend AbstractTest
, all you need to do is to override the entities
base class method:
@Override protected Class<?>[] entities() { return new Class<?>[] { Post.class, PostDetails.class, }; }
And the entities can be encapsulated in the integration tests so that each test operates on its own unique model without being affected by changes happening outside the source code of the integration test in question:
@Entity(name = "Post") @Table(name = "post") public static class Post { @Id @GeneratedValue private Long id; private String title; //Getters and setters omitted for brevity } @Entity(name = "PostDetails") @Table(name = "post_details") public static class PostDetails { @Id private Long id; @Column(name = "created_on") private Date createdOn; @Column(name = "created_by") private String createdBy; @OneToOne @MapsId private Post post; //Getters and setters omitted for brevity }
The awesomeness of this technique comes into play when you start overriding the database
base class method.
If you declare that you want to use MySQL:
@Override protected Database database() { return Database.MYSQL; }
Your test is going to use the following MySQL database schema:
create table post ( id bigint not null, title varchar(255), primary key (id) ) engine=InnoDB create table post_details ( created_by varchar(255), created_on datetime(6), post_id bigint not null, primary key (post_id) ) alter table post_details add constraint FKmcgdm1k7iriyxsq4kukebj4ei foreign key (post_id) references post (id)
If you want this test to run on PostgreSQL, just change the database
method as follows:
@Override protected Database database() { return Database.POSTGRESQL; }
And the Hibernate hbm2ddl will do the trick:
create sequence hibernate_sequence start 1 increment 1 create table post ( id int8 not null, title varchar(255), primary key (id) ) create table post_details ( created_by varchar(255), created_on timestamp, post_id int8 not null, primary key (post_id) ) alter table if exists post_details add constraint FKmcgdm1k7iriyxsq4kukebj4ei foreign key (post_id) references post
Cool, right?
JDBC
Just because you have to test JDBC code, it does not mean you code should be verbose. Thanks too the doInJDBC
method you inherit automatically from AbstractTest
, your integration test logic looks as follows:
doInJDBC(connection -> { try (Statement statement = connection.createStatement()) { statement.addBatch( "insert into post (title, version, id) " + "values ('Post no. 1', 0, 1)" ); statement.addBatch( "insert into post_comment (post_id, review, version, id) " + "values (1, 'Post comment 1.1', 0, 1)" ); statement.addBatch( "insert into post_comment (post_id, review, version, id) " + "values (1, 'Post comment 1.2', 0, 2)" ); int[] updateCounts = statement.executeBatch(); assertEquals(3, updateCounts.length); } });
Behind the scenes, the doInJDBC
method takes care of all the necessary steps to run this code:
- a database connection is acquired,
- a transaction is automatically started,
- the Java 8 lambda is executed,
- on success, the transaction is committed,
- on failure, the transaction is rolled back,
- the connection is closed no matter what happens above.
JPA and Hibernate
In the same spirit, this is how you execute JPA and Hibernate data access logic:
Post _post = doInJPA(entityManager -> { Post post = new Post("First post"); entityManager.persist(post); return post; }); doInJPA(entityManager -> { Post post = entityManager.getReference( Post.class, _post.getId() ); PostDetails details = new PostDetails("John Doe"); details.setPost(post); entityManager.persist(details); }); doInJPA(entityManager -> { PostDetails details = entityManager.find( PostDetails.class, _post.getId() ); assertNotNull(details); entityManager.flush(); details.setPost(null); });
The doInJPA
method takes care of:
- creating a JPA
EntityManager
, - starting a JPA
EntityTransaction
, - the Java 8 lambda is executed,
- on success, the
EntityTransaction
is committed, - on failure, the
EntityTransaction
is rolled back, - the
EntityManager
is closed no matter what happens above.
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
If you want to test data access code, just fork my high-performance-java-persistence GitHub repository and focus on the test logic, instead of bootstrapping JDBC, JPA or Hibernate or managing the database schema or the underlying resources involved while running your tests.
