Home

Entity, Adapter, and Descriptor

The concept of O/R mapping requires three basic building blocks:

There are very different approaches around how to express the descriptor. JPA uses annotations on entity classes, MyBatis uses XML files, and PriDE follows a different approach as you may have seen already from the quick start tutorial. The descriptor is an instance of class pm.pride.RecordDescriptor, i.e. it is code itself. This concept has a few advantages over other approaches.

A coded descriptor needs to go somewhere in your code, of course. PriDE provides two default patterns for the descriptor placement which are obvious when you think of the building blocks mentioned above: descriptors within adapter classes or descriptors within entity classes.

Descriptors in entities is what you know already from the quick start tutorial. It cases the entities to become their own adapters having their own persistence methods. This is a compact pattern which is suitable for small applications. Therefore you will find it spread over most examples provided with PriDE. The disadvantage is the same one mentioned above with JPA: the entity classes spread knowledge about database mapping information all over the code. Combined with persistence capabilities directly incorporated in entity classes, this is a questionable concept in bigger architectures.

Let’s have a look on the more sophisticated pattern of separate adapter classes. You can have a look on the general structure by generating separate classes for the quick start example table. The pure entity class can be generated by the following command:

java
-D... see quick start tutorial
pm.pride.util.generator.EntityGenerator CUSTOMER adapter.CustomerEntity -b > CustomerEntity.java

The parameter -b tells the generator to create only an entity class without descriptor. The result is an ordinary Java bean or POJO class:

package adapter;

public class CustomerEntity implements Cloneable, java.io.Serializable {
    private long id;
    private String name;
    private String firstName;

    public long getId()   { return id; }
    public String getName()   { return name; }
    public String getFirstName()   { return firstName; }

    public void setId(long id) { this.id = id; }
    public void setName(String name) { this.name = name; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    // re-constructor
    public CustomerEntity(long id) {
        setId(id);
    }

    public CustomerEntity() {}
}

The “re-constructor” is an additional constructor getting passed a value for all the attributes making up the entity’s primary key. This of interest for find operations.

Generating the corresponding adapter class looks like this:

java
-D... see quick start tutorial
pm.pride.util.generator.EntityGenerator CUSTOMER adapter.CustomerAdapter adapter.CustomerEntity > CustomerAdapter.java

The first parameter after the table name specifies the class to generate - in this case a class called CustomerAdapter in package adapter. The second parameter is the name of a entity class the adapter should refer to. The result looks like this:

package adapter;

public class CustomerAdapter extends ObjectAdapter {
    public static final String TABLE = "CUSTOMER";
    public static final String COL_ID = "id";
    public static final String COL_NAME = "name";
    public static final String COL_FIRST_NAME = "first_name";

    protected static final RecordDescriptor red =
      new RecordDescriptor(CustomerEntity.class, TABLE, null)
        .row(COL_ID, "getId", "setId")
        .row(COL_NAME, "getName", "setName")
        .row(COL_FIRST_NAME, "getFirstName", "setFirstName")
        .key(COL_ID);

    public RecordDescriptor getDescriptor() { return red; }

    CustomerAdapter(CustomerEntity entity) { super(entity); }
}

All what the adapter class has to provide is a RecordDescriptor and an optional list of column names making up the entity’s primary key. Based on that, the class inherits all entity-related persistence capabilities from class pm.pride.ObjectAdapter. Adapters always operate on an instance of the entity class which must be passed in the adapter’s constructor. Finding a customer by its primary ID looks like this when using separate adapter classes:

// Create a customer entity, initialized with a primary key value of 1
CustomerEntity customer = new CustomerEntity(1);

// Create an adapter based on the entity
CustomerAdapter adapter = new CustomerAdapter(customer);

// Call the adapter's find method to find a customer by primary key 1.
// The primary key value is read from the entity passed in the adapter's constructor
// The result (if any) is written to the same entity
adapter.find();

As you see, every persistence operation now requires one additional line of code to create the adapter. Especially when you design a multi-threaded application, it is important to know that adapter and entity instances are not supposed to be shared among multiple threads. So creating new instances in every operation is the prefered technique and is usually not a considerable code complication.

If you want to minimize the amount of code, you are free to invent your own adapter concept. Have a look on the base classes pm.pride.ObjectAdapter for the adapter above and pm.pride.MappedObject for the hybrid variant from the quick start tutorial. Both are minimalistic implementations of the mix-in pm.pride.DatabaseAdapterMixin which is the actual provider for all entity-related persistence operations. It is in turn based on the static methods of the class pm.pride.DatabaseAdapter. Using this class or the mix-in you could easily produce a generic adapter being responsible for multiple entity types similar to JPA’s EntityManager interface.

One note concerning packages: When you actually use the pattern of separate adapters in a sophisticated architecture, you should consider generating entity and adapter classes in different packages. Only the entity classes should be part of the interface for dependent code while the adapter classes should completely be hidden behind facade components as proposed in the wide-spread repository pattern.

Descriptor structure

The examples for descriptors you have seen so far should already clarify most of the descriptor structure. You will see more complicated examples in following chapters of this manual. A descriptor is assembled from the following information:

The RecordDescriptor class has a few more constructors concerned with joins and accessing multiple databases, but that’s not important for now. The basic structure described above is what you work with most of the time.

Attribute Type Mapping

The following table illustrates the mapping of Java object attribute types to SQL database field types as they are supported by PriDE. The row ‘JDBC type’ determines the type being used for the specified attribute type to access results from a JDBC ResultSet or to pass inputs to a JDBC PreparedStatement. The ‘SQL type’ specifies the actual SQL row types, the attributes can usually be mapped to. Not all SQL databases support all the mentioned type identifiers and it may also depend on the JDBC driver’s capabilities which mappings are supported. Primitive attribute types should of course only be used, if the corresponding row must not be NULL. Otherwise an exception will be thrown at runtime when attempting to process NULL values.

Java attribute type JDBC type SQL type
String String VARCHAR, VARCHAR2, NVARCHAR2, CHAR
java.util.Date java.sql.Date DATE, DATETIME, TIMESTAMP, TIME
java.sql.Date java.sql.Date DATE, DATETIME, TIMESTAMP, TIME
java.sql.Timestamp java.sql.Timestamp DATETIME, TIMESTAMP, TIME
int / Integer Integer INTEGER
float / Float Float DECIMAL, REAL
double / Double Double DECIMAL, REAL
Any enum String VARCHAR, VARCHAR2, NVARCHAR2, CHAR
boolean / Boolean Boolean BOOLEAN, INTEGER, SMALLINT, TINYINT, CHAR
BigDecimal BigDecimal DECIMAL, NUMBER
long / Long Long INTEGER, DECIMAL, NUMBER, BIGINT
short / Short Short INTEGER, SMALLINT, TINYINT, DECIMAL
byte / Byte Byte TINYINT
byte[] byte[] BLOB, LONGVARBINARY, VARBINARY
java.sql.Blob java.sql.Blob BLOB, LONGVARBINARY, VARBINARY
java.sql.Clob java.sql.Clob CLOB, LONGVARCHAR
java.sql.SQLXML / String java.sql.SQLXML java.sql.SQLXML

Clobs and Blobs can only be used through PreparedStatements, i.e. you either have to access the entities with Clob / Blob attributes with PriDE’s prepared operations or you configure PriDE to use bind variables by default (see quick start tutorial).

The precision of dates and time stamps in the database vary significantly between different database vendors. E.g. although date rows were originally intended to represent dates without time portions in SQL databases, Oracle allows seconds precision instead and so does PriDE for Oracle. When using PriDE with plain SQL, dates and timestamps are rendered by appropriate database-specific formating functions like to_date or to_timestamp in Oracle, preserving the same precision as it applies to prepared statements.

The interface pm.pride.ResourceAccessor provides the constant SYSTIME_DEFAULT. In update and insert operations this values will be translated to an expression which addresses the current database server time like CURRENT_TIMESTAMP in MySQL or SYSDATE in Oracle. This translation is only applied in plain SQL.

You can tell PriDE to map a java.util.Date attribute to an SQL time stamp by providing the JDBC type in the row definition of the record descriptor as an additional parameter like that:

.row(<columnname>, <getter>, <setter>, java.sql.Timestamp.class)

There are a few more type conversions which can be expressed that way. E.g. Enums can be represented by their ordinals in the database by providing java.lang.Integer.class as additional parameter for the mapping of the corresponding attributes.

The general pattern for arbitrary type conversion is to provide appropriate additional getter/setter pairs which encapsulate the conversion. To make clear, that these getters / setters are for internal use only, it is common practice to give the method names a leading underscore. Assume you have an enumeration type for coins with their value in cent like that:

public enum Coin {
    FIVE_CENT(5), FIFTY_CENT(50), ONE_EURO(100);
    
    private int valueInCent;
    Coin(int valueInCent) { this.valueInCent = valueInCent; }
    int value() { return valueInCent; }
}

If you map an attribute of this type to the database, PriDE expects an SQL row of type VARCHAR or a similar type to store values like ‘FIVE_CENT’ etc. If you want to represent the coins by their value in the database, you provide a type-converting getter-setter-pair for the corresponding attribute in the entity class:

class MyEntity {
    private Coin myAttr;
    
    public int _getMyAttr() {
        return myAttr.value();
    }
    
    public void _setMyAttr(int v) {
        for (Coin coin: Coin.values()) {
            if (coin.value() == v) {
                myAttr = coin;
                return;
            }
        }
        throw new IllegalArgumentException();
    }
}

Now you can use this getter setter pair to map the myAttr attribute to a DECIMAL table row, holding the coins’ values:

.row("MYATTR", "_getMyAttr", "_setMyCoin")