Andrés Testi

Wednesday, October 04, 2006

An Implicit Query Proposal for EJB3

(Spanish Version)

When we are working with ORM technologies, it's commonly suggested to separate Business Logic component in two tiers. Persistence tier groups all object that are mapped to tables in a data base. This tier acts as a data container with low functionality because it can't access to query API. In the other hand, we have the DAO (Data Access Object) tier, in charge of manage transactions, queries, insertions and updates. A good practice is to make a DAO class for each persistent class in the model component. DAO tier can see persistence tier, but persistent tier can not see DAO tier. This architecture design looks easy to develop. But objects with parent to children relationships (in navigation sense, not inheritance sense) create a new problem. If we need to update a child: Must we invoke a DAO related to the parent, or a DAO related to the child? What´s happen if we delete a parent object which must do a cascading delete over its children? Must we call the child DAO to delete each instance separately? The answer is not simple, and neither unique but it makes sense to centralize all the logic as possible in the persistence tier in order to reduce the DAO tier and minimize inconsistencies in the design pattern. A
lighter DAO could be the key to solve some of these troubles. EJB3 have the Entity Listener feature, where an entity listener accesses to entity lifecycle (CRUD) in an orthogonal way to reduce DAO responsibilities. The DAO query logic is reduced due to entity relationships that act as transparent queries. At this moment there no other contribution to the transparency in the query world.
Imagine that a salesman salary is calculated as the sum of all values of his sales. To contribute with the centralization of responsibilities, we can define a query as a named query related to Salesman class. The only tier in our architecture which is able to execute this query is the DAO tier.

@Entity
@NamedQuery(
name=”Salesman.calculateSalary”,
query=
”SELECT SUM(sale.value) “ +
“FROM Salesman s JOIN s.sales sale ” +
“WHERE s = :salesman “
)

public class Salesman{
....
@OneToMany(fetch=LAZY, cascade={ALL}, mappedBy=”salesman”)
private List getSales;
.....
}

public class Sale{
.....
@ManyToOne(fetch=LAZY, optional=false)
@JoinColumn(name=”SALESMAN_ID”)
private Salesman salesman;

@Column(name=”VALUE”)
private Integer value;
....
}

@Stateless
public class DaoSalesmanBean implements DaoSalesmanLocal{


@PersistenceContext
private EntityManager em;


public Long calculateSalary(Salesman s){
return(Long)em
.createNamedQuery(“Salesman.calculateSalary”)
.setParameter(“salesman”, s)
.getSingleResult();
}
}

Something is wrong here. Theoretically, objects in OOP have their own methods to retrieve their states. Why salesman salary must be retrieved by other object? Then, we can think in a new injection type, named Implicit Query. Imagine the next annotation type:


@Retention(RUNTIME)
@Target(METHOD)
public @interface ImplicitQuery{

String value();
String parameter() default “this”;
FetchType fetch() default EAGER;

}

Where

value: name of the related Named Query.
parameter: name of the only query parameter. By default, this value is set to “this”
fetch: fetch type.

Then, we can add the next field in Salesman class:

@ImplicitQuery(
name=”Salesman.calculateSalary”,
parameter=”salesman”
)
private Long salary;

In this way, we can eliminate calculate Salary method in DaoSalesman class, reducing even more the responsabilities in DAO tier!!!


Implicit Queries Advantages:
  • Transparent Subqueries:

Imagine that a salesman take several salary advances, then we want to known which salary advance are greatest than salesman sales value sum. In the current EJB3 specification, we must work as follow:

SELECT s.salaryAdvance
FROM Salesman s
WHERE s = :salesman AND s.salaryAdvance.value > ALL(
SELECT SUM(sale.value)
FROM Sale sale
WHERE sale.salesman = :salesman
)

But with an Implicit Query feature, the query looks as follow:

SELECT s.salaryAdvance
FROM Salesman s
WHERE s = :salesman AND s.salaryAdvance.value > s.salary

This is a more elegant solution than previous solution.

  • Typesafe:

If we associate an Implicit Query to a field or to a getter, EJB3 container can detect type inconsistency of property and query return in deployment time. This feature, aids us to early detect type errors.

For example:

// these lines throws a deployment time exception, because
// property type is String, but the query returns a Long
@ImplicitQuery(name=”Salesman.calculateSalary”)
private String salary;

  • Polimorphism:

If, besides a salesmen, our enterprise has administrative employees, and their salaries are calculated as sum of their working hours, we can generalized both classes in a Employee superclass.

Declarations become as follows:

public class Employee{...}

@Entity

@NamedQuery(
name=”AdministrativeEmployee.calculateSalary”,
query=
”SELECT SUM(wh.value) ” +
“FROM “ +
“ AdministrativeEmployee a “ +
“ JOIN a.workingHours wh “ +
“WHERE a = :admEmployee”
)
public class AdministrativeEmployee extends Employee{...}

@Entity
@NamedQuery(
name=”Salesman.calculalteSalary”,
query=
”SELECT SUM(sale.value) “ +
“FROM Salesman s JOIN s.sales sale ” +
“WHERE s = :salesman “
)
public class Salesman extends Employee{....}

In current EJB3 specification , DaoEmployee looks as follows:

public Long calculateSalary(Employee e){
if(e instanceof AdministrativeEmployee){
return(Long)em.createNamedQuery(
“AdministrativeEmployee.calculateSalary”
).setParameter(“admEmployee”, e).getResultList();
}else{
return(Long)em
.createNamedQuery(“Salesman.calculateSalary”)
.setParameter(“salesman”, e)
.getResultList();
}
}

As you can see, this solution is distant to OOP good practices. Design can result more complex, if there are more Employee subclasses. With an implicit query feature, this method is not necessary, simply you must define an Implicit Query for each Employee subclass, then the result is obtained as follows:

employee.getSalary();

In an EJBQL context, we can economize a lot of queries. For example, if we need to find all employees with a salary lower than 3000 without an implicit query feature, we must to write a subquery for each employee subclass:

SELECT DISTINCT(e)
FROM AdministrativeEmployee a, Salesman s
WHERE (
a = :employee AND 3000 > (
SELECT SUM(wh.value)
FROM
AdministrativeEmployee a1
JOIN a1.workingHours wh
WHERE a1 = :employee
)
)OR(
s = :employee AND 3000 > (
SELECT SUM(sale.value)
FROM Salesman s1 JOIN s1.sales sale
WHERE s1 = :salesman
)
)

Having an Implicit Query feature, previous code is reduced to:

SELECT e
FROM Employee e
WHERE e.salary < 3000


  • Simplified expressions in Bean contexts:

Implicit Query, simplifies access to queries in the Expression Language, because it's possible to access the query result simply accessing a bean property.

3 Comments:

  • Interesting post. Not sure whether I have understood everything, but doesn't your feature request boil down to the more general case of Domain Driven Development ?

    (http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215)

    Basically, the idea is that the core classes (that are also persisted) are intelligent, and have the behavior built-in.

    However, it doesn't mean that technical code is written inside the domain. It is possible to use Frameworks such as Spring 2 and AspectJ integration to inject dependencies to domain objects..

    So, for your case, you would have the specific queries implemented inside DAOs, or other technical classes, independant of the implementation, and the reference to the DAos would be injected to the domain classes.

    By Blogger samokk, at 9:09 AM  

  • I think "virtual field" is a better name.
    Do you think so?

    By Blogger fkpwolf, at 6:10 PM  

  • mmmm iqual to LinQ

    By Blogger .::[Tomas]::., at 6:03 AM  

Post a Comment

<< Home