OR-Broker a flexible way for binding SQL and Java
OR-Broker is yet another library trying to solve the mapping problem between Java objects and tables in a relational database.
The reason for it?
I had the need to tackle a big and legacy (un-touchable) DB schema but I didn’t want to mirror it in the application data-model. I wanted total freedom in my data-model: binding factory or multi params mehtods, handling inheritance. Moreover I wanted total control over the executed SQL statemets (on multiple datasources), and I did want to keep my SQL DRY.
Know it
Build your own query in plain old SQL (wrapped in an XML envelope)
<statement id="selectCompany" result="company">
<content>
SELECT c.*
FROM company c
</content>
<condition id="ById">
WHERE c.id = :id
</condition>
</statement>
Configure the data binding using properties or methods of your data-model
<binding id="company" class="custom.datamodel.Company">
<property name="id">
<!-- Explicit type convertion -->
<column name="id" as="java.lang.Long"/>
</property>
<!-- set property -->
<property name="name">
<column name="company_name"/>
</property>
<!-- bind column by property name -->
<property name="address"/>
<!--
Alternative method syntax ia available
<method name="setAddress">
<column name="address" />
</method>
-->
</binding>
Perform queries using the library API
Map params = new HashMap();
params.put("id", 1);
Company company =
broker.fetchOne("selectCompanyById", params, dataSource);
The mapping will be performed by the OR-broker using reflection.
Keep your SQL DRY
Add multiple WHERE
in one SQL statements
<statement id="selectEmployee" result="employee">
<content>
SELECT
e.*,
c.* as company_name,
u.name as bu
FROM employee e
LEFT JOIN company c on c.id = e.company_id
LEFT JOIN unit u on u.employee_id = e.id
</content>
<!-- multiple WHEREs -->
<condition id="ByCompanyId">
WHERE e.company_id = :id
</condition>
<condition id="ById">
WHERE e.id = :id
</condition>
<append>
ORDER BY e.id DESC
</append>
</statement>
and use them accordingly
// Call the 'selectEmployee' statement using the 'ById' condition
Employee employee =
broker.<Employee>fetchOne("selectEmployeeById", params, dataSource);
Share WHERE
conditions between statements
<statement id="countEmployee" result="company">
<content>
SELECT count(*)
FROM employee e
LEFT JOIN company c on c.id = e.company_id
</content>
<!-- inherits where conditions from the
selectEmployee statements -->
<conditions from="selectEmployee"/>
</statement>
to avoid duplications in SQL.
Use Velocity templating in your statements
<statement id="insertCompany">
<content>
INSERT INTO company(
#foreach($f in $fields)
`$f` #if( $velocityHasNext ),#end
#end
) VALUES (
#foreach($f in $fields)
:$f #if( $velocityHasNext ),#end
#end
)
</content>
</statement>
to build repetitive SQL queries.
Freedom for objects
Bind DB tables and your data-model using multi-parameter methods, call factory methods, deal with inheritance
<binding id="employee" class="custom.datamodel.Employee">
<property name="name"/>
<property name="email">
<column name="email"/>
</property>
<!-- Call the given factory method
to value the 'type' property (it is an Enum type)-->
<property name="type" factory="custom.datamodel.EmployeeType" method="valueOf">
<column name="type"/>
</property>
...
<!-- call a method using two TABLE columns as parameters -->
<method name="setSalary">
<column name="salary" />
<column name="currency"/>
</method>
<!-- Return an instance of Manager (Manager extends Employee)
if column named type has value 0 -->
<extend with="manager" ifequals="0">
<column name="type" />
</extend>
</binding>
Write your own adapter for seamless type conversion between DB columns types and your classes
package org.orbroker.binding.adapter.impl;
import java.sql.Timestamp;
import java.util.Date;
import org.orbroker.binding.adapter.BindingAdapter;
public class TimestampToDateAdapter implements BindingAdapter<Timestamp, Date> {
@Override
public Class<Timestamp> from() {
return Timestamp.class;
}
@Override
public Class<Date> to() {
return Date.class;
}
@Override
public Date valueOf(Object from) {
return new Date(((Timestamp)from).getTime());
}
}
they will loaded and auto-magically applied when a data-type conversion is needed.
<binding id="employee" class="custom.datamodel.Employee">
...
<!-- JDBC type for the 'birth' colunm is java.sql.Timestamp,
Employee.birtDate property type is java.util.Date-->
<property name="birthDate">
<column name="birth"/>
</property>
...
</binding>
as in the example above.
Use it
It’s a Maven project. Just download it to your machine and install into your maven repository
$ wget -O orbroker.zip https://github.com/flerro/orbroker/archive/master.zip
$ unzip orbroker.zip
$ cd orbroker-master
orbroker-master/ $ mvn install
and add a reference to OR-broker as a dependency
<dependency>
<groupId>org.orbroker</groupId>
<artifactId>orbroker</artifactId>
<version>1.0</version>
</dependency>
in your project pom.xml
.
Status
Is it production ready? It is quite solid, please take a look at it and decide by yourself. It has been built (and used) to power a big API project handling lots of traffic.
Behind the scenes
OR-Broker leverages:
- Spring reflection utils for binding
- velocity for optional SQL templating
- standard Java XML SAX parser
If you are not familiar with maven please take a look to: Maven in five minutes.
Known issues/drawbacks
- too much freedom means lots of XML boilerplate
- documentation is missing (please take a look at the tests for examples)
- XML is quite verbose (a DSL would be way better)
Download
Feel free to use/fork OR-Broker on Github, I’m looking forward to your pull requests and to your feedback.