A beginner’s guide to the high-performance-java-persistence GitHub repository

(Last Updated On: January 31, 2018)

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.

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’s gives you a great advantage when switching the 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.

Subscribe to our Newsletter

* indicates required
10 000 readers have found this blog worth following!

If you subscribe to my newsletter, you'll get:
  • A free sample of my Video Course about running Integration tests at warp-speed using Docker and tmpfs
  • 3 chapters from my book, High-Performance Java Persistence, 
  • a 10% discount coupon for my book. 
Get the most out of your persistence layer!

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *