Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Basics of Using Hrorm

DZone 's Guide to

The Basics of Using Hrorm

Let's explore the basics of using Hrorm.

· Database Zone ·
Free Resource

In an earlier article, I introduced Hrorm, a simple, declarative, type-checked library for building DAOs. Hrorm is an ORM tool condensed down to its essence: making SQL queries against an RDBMS and translating their results into objects. The first article was about why a new ORM tool was worthwhile, even when we already have many options. If that was the introduction to Hrorm, this is chapter one: how to use the basics.

Preliminaries

Before using any development tool, there are a number of things that have to be done: getting it, installing it, configuring it, getting its dependencies (and resolving dependency conflicts), etc. In the Java world, Maven has taken care of a lot of that for us, and of course, you can get Hrorm via Maven. Of course, you can also just download the jar file and put it in your classpath using whatever means you wish.

The most difficult part of setting up Hrorm is setting up your JDBC provider. This is beyond the scope of this article since that varies from database to database. But once you have done that, and can create a java.sql.Connection to your datastore, Hrorm is ready to go. Hrorm is a single jar, it just needs to be in your classpath. Hrorm has no dependencies: it just uses the Java standard libraries. Hrorm requires no container, no configuration, and since it uses the JDBC interfaces and ANSI SQL, there is no per-database customization. Moreover, Hrorm works with plain Java objects: it does not require your object model contain any Hrorm code, annotations, or implement any of its interfaces.

Example Model

For the rest of this article, I will be talking about a simple entity model. Here's the Java, with getters and setters omitted (though some accessors are required, Hrorm does not use reflection and especially not sun.misc.Unsafe):

    class Product {
        private Long id;
        private String name;
        private ProductCategory category;
        private BigDecimal price;
        private long sku;
        private boolean discontinued;
        private LocalDateTime firstAvailable;
    }

    enum ProductCategory {
        Kitchen,Clothing,Electronic,Miscellaneous
    }

And here's the SQL schema.

    create sequence products_sequence;

    create table products (
        id integer PRIMARY KEY,
        name text,
        category text,
        price decimal,
        sku integer,
        discontinued boolean,
        first_available timestamp
    );

As is often the case with examples of this type, it's a bit silly and artificial, but it will highlight a number of the basic features of Hrorm.

Defining an Entity in Hrorm

To manage the persistence of an entity using Hrorm, we first create a DaoBuilder. A DaoBuilder uses a fluent, declarative interface to describe the entity's database schema and Java object model allowing Hrorm to do persistence tasks.

Most of the mapping we will need will be straightforward, but there is one detail that will require some preliminaries. Because the Java object model contains a custom enumerated type and the database contains a string, we need to implement a simple interface, org.hrorm.Converter<CLASS, CODE>, that will handle the translation to and from a String and the enumerated type. The implementation is trivial and shown below.

    class CategoryConverter implements Converter<ProductCategory, String> {
        @Override
        public String from(ProductCategory item) {
            return item.toString();
        }

        @Override
        public ProductCategory to(String aString) {
            return ProductCategory.valueOf(aString);
        }
    }

The other types are directly supported. The DaoBuilder looks like this.

    DaoBuilder<Product> productDaoBuilder = new DaoBuilder<>("products", Product::new)
            .withPrimaryKey("id", "products_sequence", Product::getId, Product::setId)
            .withStringColumn("name", Product::getName, Product::setName)
            .withConvertingStringColumn("category", Product::getCategory, Product::setCategory, new CategoryConverter())
            .withBigDecimalColumn("price", Product::getPrice, Product::setPrice)
            .withIntegerColumn("sku", Product::getSku, Product::setSku)
            .withBooleanColumn("discontinued", Product::isDiscontinued, Product::setDiscontinued)
            .withLocalDateTimeColumn("first_available", Product::getFirstAvailable, Product::setFirstAvailable);

The DaoBuilder is constructed with the name of the underlying table, and the function reference to the constructor of the Java object (Hrorm also supports a mechanism for immutable objects without no argument constructors, and you can read more about that in the hrorm documentation).

The next line defines the primary key of the object, including the name of the column, the name of the sequence that will populate it, and the getter and setter for the primary key. The remaining lines define what column names correspond with the getters and setters on the Java object. The correct types must be used. The ProductCategory also includes an instance of the converter shown above.

Using Hrorm

This is all the setup Hrorm needs to manage persistence of a Product object. As the name implies, a DaoBuilder is used to build actual Dao objects. To do this, we need one other thing: a java.sql.Connection object, as provided by the JDBC implementation of whatever database being used. Up until now, we have been working for Hrorm, but now Hrorm will start working for us.

    Connection connection = // somehow we get the connection from the JDBC
    Dao<Product> productDao = productDaoBuilder.buildDao(connection);

Our first job is to persist an instance of a product. We create a new instance of the Product object, without setting the primary key id field. We pass that to the Dao.insert() method. The insert method will do several things.

  1. It will select a new value from the products_sequence in the database.
  2. It will run SQL on a prepared statement in the database that looks like INSERT INTO PRODUCTS (ID, NAME, ...) VALUES ( ... ).
  3. It will set the value of the new primary key onto the Product object.
    Product product = new Product();
    product.setName("Chef Knife");
    product.setCategory(ProductCategory.Kitchen);
    product.setPrice(new BigDecimal("99.95"));
    product.setSku(12345L);
    product.setDiscontinued(false);
    product.setFirstAvailable(LocalDateTime.of(2017, 6, 15, 0, 0));

    long chefKnifeId = dao.insert(product);

    connection.commit();

Note that Hrorm does not take responsibility for when to commit. It leaves that up to you since the application knows what has to be a transaction, but Hrorm does not.

Later on, when the product goes on sale, we can load it up, set the new price, and issue an update.

    Product chefKnife = productDao.select(chefKnifeId);
    chefKnife.setPrice(new BigDecimal("59.95"));

    productDao.update(chefKnife);

    connection.commit();

Hrorm does not do any state tracking or try to issue optimized SQL. When the Dao.update() method is called, it will update all the fields in the database for the record with the primary key matching the object.

Hrorm also supports a delete method, that issues DELETE SQL for the particular record.

Various Selects

The methods to insert, update, and delete records are quite straightforward, and there is little to add to what was said above. The case of select is a bit more complicated. We have already seen one select method above when a single record was pulled from the database by its primary key. But Hrorm supports a variety of mechanisms for doing selects.

First up, a way to select matching records for some template object. The idea here is to create an instance of the entity class, populated with some values, and then instruct Hrorm to find the matching records by some subset of the table columns. For instance, if we wanted to find all the electronic products that are discontinued, we could write code like this.

    Dao<Product> productDao = productDaoBuilder.buildDao(connection);

    Product template = new Product();
    template.setCategory(ProductCategory.Electronic);
    template.setDiscontinued(true);

    List<Product> products = productDao.selectManyByColumns(template, "category", "discontinued");

This is a useful mechanism when you have an object instance that you want to match, but it is not the most generic method that Hrorm makes available. Selecting by columns only allows for checking for equality of values. Using Hrorm's Where objects make it possible to write much more general queries.

Let's say we wanted to find all the 'Miscellaneous' products that cost less than $100 and were introduced in 2018. Using a Where object will allow us to construct the query we need as follows.

    List<Product> products = productDao.select(
        new Where("category", Operator.EQUALS, ProductCategory.Miscellaneous.toString())
                .and("price", Operator.LESS_THAN, new BigDecimal("100.00"))
                .and("first_available", Operator.GREATER_THAN_OR_EQUALS, LocalDateTime.of(2018,1,1,0,0))
                .and("first_available", Operator.LESS_THAN_OR_EQUALS, LocalDateTime.of(2018,12,31,23,59)));

A Hrorm Where object is a (possibly nested) collection of predicates on the columns. Using the org.hrorm.Operator class we describe what test to use for the given value. Predicates are joined using either the or or the and methods.

The mechanisms shown above will always result in the construction of a list of objects equal to the size of the result set. If you wish to run a query and make a computation on the results without ever instantiating all the results in one big list, you can use the Dao.foldingSelect() method. If you are not familiar with folding, think of the java.util.Stream.reduce() methods. The idea is to provide a function that acts as an accumulator and it will pass over each value in the result set in turn, but only use the result for its calculation, not keep it around in memory.

The following example would compute the total of all the prices of all the discontinued items (Admittedly, this example is ridiculous, why would you ever do this? But the point is to show the API in the simplest way).

    BigDecimal accumulatedPrice = productDao.foldingSelect(
            new BigDecimal(0),
            (accumulatedCost, product) -> accumulatedCost.add(product.getPrice()),
            new Where("discontinued", Operator.EQUALS, false));

The above example is silly in real terms, but it's also silly because it could be accomplished more simply by running a function within SQL.

    BigDecimal accumulatedPrice = productDao.runBigDecimalFunction(
            SqlFunction.SUM,
            "price",
            new Where("discontinued", Operator.EQUALS, false));

There are several other details that you can learn about by reading the Hrorm documentation and Javadocs, but the above examples give you a good idea of the abilities Hrorm presents. After defining a DaoBuilder in about 8 lines of code, Hrorm provides a decent toolkit for doing CRUD operations and never requires us to write SQL or parse a java.sql.ResultSet. Everything is done in a fluent, expressive idiom.

Next Steps

The above gives the flavor of how you use Hrorm to do basic CRUD operations on a simple model backed by one table. But the purpose of an RDBMS is to manage relations. In the final article, I will describe how to use Hrorm to model complex entities that have relationships. If you just cannot wait, you can read the documentation today.

Topics:
java ,orm ,sql ,database ,tutorial ,hrorm ,orm tool

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}