The Data Acccess Objects (DAOs) are part of the persistence layer.
They are responsible for a specific entity and should be named «Entity»Dao
and «Entity»DaoImpl
.
The DAO offers the so called CRUD-functionalities (create, retrieve, update, delete) for the corresponding entity.
Additionally a DAO may offer advanced operations such as query or locking methods.
For each DAO there is an interface named «Entity»Dao
that defines the API. For CRUD support and common naming we derive it from the ApplicationDao
interface that comes with the devon application template:
public interface MyEntityDao extends ApplicationDao<MyEntity> {
List<MyEntity> findByCriteria(MyEntitySearchCriteria criteria);
}
All CRUD operations are inherited from ApplicationDao
so you only have to declare the additional methods.
Implementing a DAO is quite simple. We create a class named «Entity»DaoImpl
that extends ApplicationDaoImpl
and implements your «Entity»Dao
interface:
public class MyEntityDaoImpl extends ApplicationDaoImpl<MyEntity> implements MyEntityDao {
public List<MyEntity> findByCriteria(MyEntitySearchCriteria criteria) {
TypedQuery<MyEntity> query = createQuery(criteria, getEntityManager());
return query.getResultList();
}
...
}
Again you only need to implement the additional non-CRUD methods that you have declared in your «Entity»Dao
interface.
In the DAO implementation you can use the method getEntityManager()
to access the EntityManager
from the JPA. You will need the EntityManager
to create and execute queries.
All static queries are declared in the file src\main\resources\META-INF\orm.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">
<named-query name="find.dish.with.max.price">
<query><![SELECT dish FROM DishEntity dish WHERE dish.price <= :maxPrice]]></query>
</named-query>
...
</hibernate-mapping>
When your application is started, all these static queries will be created as prepared statements. This allows better performance and also ensures that you get errors for invalid JPQL queries when you start your app rather than later when the query is used.
To avoid redundant occurrences of the query name (get.open.order.positions.for.order
) we define a constant for each named query:
public class NamedQueries {
public static final String FIND_DISH_WITH_MAX_PRICE = "find.dish.with.max.price";
}
Note that changing the name of the java constant (FIND_DISH_WITH_MAX_PRICE
) can be done easily with refactoring. Further you can trace where the query is used by searching the references of the constant.
The following listing shows how to use this query:
public List<DishEntity> findDishByMaxPrice(BigDecimal maxPrice) {
Query query = getEntityManager().createNamedQuery(NamedQueries.FIND_DISH_WITH_MAX_PRICE);
query.setParameter("maxPrice", maxPrice);
return query.getResultList();
}
Via EntityManager.createNamedQuery(String)
we create an instance of Query
for our predefined static query.
Next we use setParameter(String, Object)
to provide a parameter (maxPrice
) to the query. This has to be done for all parameters of the query.
Note that using the createQuery(String)
method, which takes the entire query as string (that may already contain the parameter) is not allowed to avoid SQL injection vulnerabilities.
When the method getResultList()
is invoked, the query is executed and the result is delivered as List
. As an alternative, there is a method called getSingleResult()
, which returns the entity if the query returned exactly one and throws an exception otherwise.