One of the things that Hibernate is quite good at is CRUD, i.e. persisting object graphs to the database. This is particularly true if your application runs in a Java domain-model-driven context. Your models are required to adhere to the standards set by JPA/Hibernate, of course. The same applies to mapping relational-model-driven data onto complex object graphs in memory. Again, you’ll have to adhere to the standards set by JPA/Hibernate.
If you’re operating on rather complex relational models, mapping data onto rather complex domain models, then you might want to get back in control of the mapping process, as auto-mapping will cause more headaches than it solves problems. An interesting approach has been shown recently on the
ModelMapper website in an
example integration with
jOOQ. (note, there is also an
example integration with
JDBI). With the permission of the author Jonathan Halterman, I’m citing this interesting example:
jOOQ Integration
ModelMapper’s jOOQ integration allows you to map a jOOQ
Record to a JavaBean.
Setup
To get started, add the
modelmapper-jooq
Maven dependency to your project:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>0.6.1</version>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper-jooq</artifactId>
<version>0.6.1</version>
</dependency>
Next, configure ModelMapper to support the RecordValueReader, which allows for values to be read and mapped from a jOOQ
Record:
modelMapper.getConfiguration()
.addValueReader(new RecordValueReader());
Example Mapping
Now let’s see an example mapping of a jOOQ record to a JavaBean. Consider the following record representing an order:
order_id |
customer_id |
customer_address_street |
customer_address_city |
345 |
678 |
123 Main Street |
SF |
We may need to map this to a more complex object model:
// Assume getters and setters are present
public class Order {
private int id;
private Customer customer;
}
public class Customer {
private Address address;
}
public class Address {
private String street;
private String city;
}
Since the source Record’s fields in this example uses an underscore naming convention, we’ll need to configure ModelMapper to tokenize source property names by underscore:
modelMapper
.getConfiguration()
.setSourceNameTokenizer(NameTokenizers.UNDERSCORE);
With that set, mapping an order Record to an Order object is simple:
Order order =
modelMapper.map(orderRecord, Order.class);
And we can assert that values are mapped as expected:
assertEquals(456, order.getId());
assertEquals(789, order.getCustomer().getId());
assertEquals("123 Main Street",
order.getCustomer()
.getAddress()
.getStreet());
assertEquals("SF",
order.getCustomer()
.getAddress()
.getCity());
Explicit Mapping
While ModelMapper will do its best to implicitly match Record values to destination properties, sometimes you may need to explicitly define mappings between properties.
Let’s map our Record’s
customer_address_street
to
Order.customer.address.street
:
PropertyMap<Record, Order> orderMap =
new PropertyMap<Record, Order>() {
protected void configure() {
map(source("customer_address_street"))
.getCustomer()
.getAddress()
.setStreet(null);
}
};
Then we can add the mapping to our
ModelMapper
instance for the
orderRecord
:
modelMapper.createTypeMap(orderRecord, Order.class)
.addMappings(orderMap);
(see the
ModelMapper manual pages for more details about property mapping)
Things to Note
ModelMapper maintains a
TypeMap for each source and destination type, containing the mappings bewteen the two types. For “generic” types such as Record this can be problematic since the structure of a Record can vary. In order to distinguish structurally different Records that map to the same destination type, we can provide a
type map name to ModelMapper.
Continuing with the example above, let’s map another order Record, this one with a different structure, to the same Order class:
order_id |
order_customer_id |
order_customer_address_street |
order_customer_address_city |
444 |
777 |
123 Main Street |
LA |
Mapping this Record to an order is simple, but we’ll need to provide a
type map name to distinguish this Record to Order mapping from the previous unnamed mapping:
Order order = modelMapper.map(
longOrderRecord, Order.class, "long");
Example taken from:
http://modelmapper.org/user-manual/jooq-integration/
More Examples
When choosing ModelMapper, you’re not just chosing an API to map relational data to your domain model. ModelMapper is designed for arbitrary model transformation, which can make it a strategic choice for your stack.
Check out this marvelous Open Source gem on the
ModelMapper website.
Like this:
Like Loading...
Hi, I’m using Jooq with model mapper and guice in a quite big application. I’using model Jooq 3.1.0, mapper 0.6.1 and guice 3.0.
I followed these instructions but the following test fails:
class MiaClasse {
private String asd;
private String email;
//get and set here ….
}
@Test
public void jooqRecordModelMapperTest() throws Exception {
Result<Record2> res = JooqUtil.getDSLContext(db.getConnection())
.select(ACCOUNT_USER.PASSWORD_HASH, ACCOUNT_USER.EMAIL)
.from(ACCOUNT_USER)
.fetch();
ModelMapper mp = new ModelMapper();
mp.getConfiguration().addValueReader(new RecordValueReader());
mp.getConfiguration().setSourceNameTokenizer(NameTokenizers.UNDERSCORE);
mp.getConfiguration().setProvider(GuiceIntegration.fromGuice(injector));
mp.addMappings(new PropertyMap() {
@Override
protected void configure() {
map(source(“password_hash”)).setAsd(null);
}
});
for (Record2 r : res) {
MiaClasse c = mp.map(r, MiaClasse.class);
assertThat(c.getAsd(), is(r.value1()));
assertThat(c.getEmail(), is(r.value2()));
}
}
to get it pass i need to use a PropertyMap instead of PropertyMap.
But in Jooq 3.1.0 RecordImpl class is a package protected, so I need to “patch” it including this one in my source code tree and set the public visibility.
Can you make this class public?
Thanks for your interest in jOOQ. Your best way to get community feedback is by using the jOOQ user group:
https://groups.google.com/forum/#!forum/jooq-user
Simply paste the above in an e-mail to the group.