Java EE 8 Design Patterns and Best Practices
上QQ阅读APP看书,第一时间看更新

Implementing DAO

In order to reuse a lot of code and promote best practices to implement DAO, we will create an abstract DAO, called AbstractDao, which is the superclass of all DAOs, that has methods with the generic logic that can be used by all DAOs:

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public abstract class AbstractDao <T extends Entity>{

//EntityManager that provide JPA functionalities
@PersistenceContext
protected EntityManager em;

//Get the type of Subclass that implements Entity interface
protected Class<T> getType() {
ParameterizedType genericType = (ParameterizedType)
this.getClass().getGenericSuperclass();
return (Class<T>) genericType.getActualTypeArguments()[0];
}

//Find entity filtering by id.
public Optional<T> findById ( T entity ){

return Optional.ofNullable( em.find( (Class<T>)
entity.getClass(), entity.getId() ) );

}

public Optional<T> persist (T entity ){

em.persist( entity );
return Optional.of( entity );

}

public Optional<T> update ( T entity ){

return Optional.ofNullable( em.merge( entity ) );

}

public void delete ( T entity ){
em.remove( entity );
}


protected List<T> listWithNamedQuery(String namedQuery, Map<String,
Object> parameters){


TypedQuery<T> query = em.createNamedQuery( namedQuery,
getType() );
parameters.keySet().stream().forEach( key-> query.setParameter(
key, parameters.get( key ) ) );
return query.getResultList();

}

protected Optional<T> findWithNamedQuery(String namedQuery,
Map<String, Object> parameters){

TypedQuery<T> query = em.createNamedQuery( namedQuery,
getType() );
parameters.keySet().stream().forEach( key-> query.setParameter(
key, parameters.get( key ) ) );
return Optional.ofNullable(query.getSingleResult());

}

}

To prevent the user from instantiating AbstractDao, we created the class in the preceding code as an abstract class. Another AbstractDao characteristic is the return of methods, which only return Entity or a list of Entity. This is a good practice, because we know the object type that is returned by this method and it organizes our code, because we know what type of value our method could return:

import javax.ejb.Stateless;
import java.util.Collections;
import java.util.List;

@Stateless
public class EmployeeDao extends AbstractDao <Employee> {

public List<Employee> findByName(String name ){

return this.listWithNamedQuery("Employee.findByName",
Collections.singletonMap(
"name", name ) );

}

public List<Employee> findAll(){

return this.listWithNamedQuery("Employee.findAll",
Collections.emptyMap());

}

}

We can see that we have the EmployeeDao class, which is the DAO that reads and writes data about Employee. This class is an EJB, meaning that it can control all transactions with the JTA specification—assuming that transactions are managed by the Java EE container—and make the transaction control transparent to the application. To read employee data, we can call the findAll, findByName, and findById methods; to write employee data, we can call the persist and update methods; and to remove (delete) employee data, we can call the delete method. Note that the business class—a class that acts on the business tier—doesn't know about the process of reading and writing data; it only knows the parameters of the method to call as well as its returns. Therefore, we can substitute the data source without impacting the business logic. We have EmployeeBusiness using the DAO to read and write data, which we will see here:

import com.packt.javaee8.dao.EmployeeDao;
import com.packt.javaee8.entity.Employee;


import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;

@Stateless
public class EmployeeBusiness{

@Inject
protected EmployeeDao employeeDao;


public List<Employee> listByName( String name ){

return employeeDao.findByName( name );

}

public boolean save ( Employee employee ){

return employeeDao.persist( employee ).isPresent();

}

public List<Employee> listAll(){

return employeeDao.findAll();

}

public Optional<Employee> findById(Employee employee ){
return employeeDao.findById(employee);
}
}

This is a good pattern; its use is very broad and most applications implement it. When implementing this pattern, be careful not to expose data-source characteristics, for example, by sending a SQL query as a parameter to DAO's execution or by sending a path of the file system as a parameter to DAO's execution.