Persism

Logo

A zero ceremony ORM for Java

View the Project on GitHub sproket/Persism

Using Records with Persism

Records are an exiting new feature of Java 16 (previewed in 14 and 15) that allow us to write simple immutable data classes with little ceremony.

Reading records:

Suppose we have some query we want to get results for:

SELECT c.Customer_ID, c.Company_Name, o.ID Order_ID, o.Name AS Description, 
        o.Date_Paid, o.Created AS DateCreated, o.PAID      
    FROM Orders o
    JOIN Customers c ON o.Customer_ID = c.Customer_ID

We can write a record now to represent this result.

@NotTable
public record CustomerOrder(String customerId, 
                            String companyName, 
                            String description, 
                            long orderId,
                            LocalDateTime dateCreated, 
                            Date datePaid, 
                            boolean paid) {
}

That’s all there is to it. A record can be this simple. Persism treats this exactly the same a normal POJO except that records are immutable.

You can also supply other constructors for queries. Let’s assume you have another query and want to use this same record but maybe without the Date_Paid, DateCreated and PAID columns. OK.

@NotTable
public record CustomerOrder(String customerId, 
                            String companyName, 
                            String description, 
                            long orderId,
                            LocalDateTime dateCreated, 
                            Date datePaid, 
                            boolean paid) {
                            
    // Add another constructor
    @ConstructorProperties({"customerId", "companyName", "description", "orderId"})
    public CustomerOrderRec(String customerId, String companyName, String description, long orderId) {
        this(customerId, companyName, description, orderId, null, null, false);
    }
                           
}

Now this same record can be used with a different query:

SELECT c.Customer_ID, c.Company_Name, o.ID Order_ID, o.Name AS Description 
    FROM Orders o
    JOIN Customers c ON o.Customer_ID = c.Customer_ID

Note in this case we need to supply the property name parameters because by default Java does not compile parameter names into the compiled class files. Use the @ConstructorProperties for this. This a standard annotation in the java.beans package. The other way to do this is to compile your project with “-parameters” but this is a non-standard compiler switch - it may not work on every jvm / platform.

Note that neither the order of the queried columns, nor the order of the record constructor matter. Persism will match up the columns to the record properties.

Note: You can’t use the simple fetch method with a record since we expect a mutable object. If you try it you’ll get "Cannot read a Record type object with this method." Exception.

Using Records for Tables

Insert records:

You can insert a record the same way you would with a normal POJO. The only thing that’s different is that Persism can’t modify the original afterward if you had auto increment or other columns with defaults. The insert method now returns a Result object containing the row change count (as before) and the updated record object.

Result<Invoice> result = session.insert(invoice);

assertTrue("rows s/b > 0", result.rows() > 0);
assertTrue("Invoice ID > 0", result.dataObject().invoiceId() > 0);
assertNotNull("Created s/b not null", result.dataObject().dateCreated());

Update records:

Since you can’t modify a record, if you want to make changes you need to instantiate a new record and call the session update method as usual.

Invoice oldInvoice...

Invoice invoice = new Invoice(oldInvoice.id(), oldInvoice.custId(), etc....)

session.update(invoice);

You could also add a copy constructor to your record for this purpose.

Delete records:

Delete works the same as with normal POJOs.

Notes

The @Column and @NotColumn annotation are supported. Not sure why you’d need @NotColumn though.

Adding calculated fields is easy with Records!

public record OrderItem(int orderId, String description, int qty, double price) {

    // calculated field
    public double total() {
        return price * qty;
    }
}