java - generic - Hibernate: CRUD Genérico DAO
hibernate java ejemplo (5)
Mi aplicación web tiene muchas tablas / entidades de servicio, como payment_methods
, tax_codes
, province_codes
, etc.
Cada vez que agrego una nueva entidad, tengo que escribir un DAO. El caso es que, básicamente, son todos iguales, pero la única diferencia es la clase de entidad en sí misma .
Sé que las herramientas de Hibernate pueden generar el código automáticamente, pero no puedo usarlas ahora (no pregunto por qué), así que estoy pensando en un DAO genérico . Hay mucha literatura sobre eso, pero no puedo juntar piezas y hacer que funcione con Spring.
Se trata de genéricos, creo, tendrá cuatro métodos básicos:
-
listAll
-
saveOrUpdate
-
deleteById
-
getById
y eso es todo.
Pregunta:
¿Cuál es la mejor práctica para no reinventar la rueda? ¿Todavía no hay algo listo para usar?
Aquí está el mío
@Component
public class Dao{
@Resource(name = "sessionFactory")
private SessionFactory sessionFactory;
public <T> T save(final T o){
return (T) sessionFactory.getCurrentSession().save(o);
}
public void delete(final Object object){
sessionFactory.getCurrentSession().delete(object);
}
/***/
public <T> T get(final Class<T> type, final Long id){
return (T) sessionFactory.getCurrentSession().get(type, id);
}
/***/
public <T> T merge(final T o) {
return (T) sessionFactory.getCurrentSession().merge(o);
}
/***/
public <T> void saveOrUpdate(final T o){
sessionFactory.getCurrentSession().saveOrUpdate(o);
}
public <T> List<T> getAll(final Class<T> type) {
final Session session = sessionFactory.getCurrentSession();
final Criteria crit = session.createCriteria(type);
return crit.list();
}
// and so on, you shoudl get the idea
y luego puede acceder como en la capa de servicio:
@Autowired
private Dao dao;
@Transactional(readOnly = true)
public List<MyEntity> getAll() {
return dao.getAll(MyEntity.class);
}
No escriba dao específico para cada entidad. Puede implementar un DAO genérico que realiza el 90% del trabajo para todas las entidades que necesita. Puede ampliarlo en caso de que desee un tratamiento específico de ciertas entidades.
En el proyecto en el que estoy trabajando actualmente tenemos dicho DAO que envuelve la sesión de Hibernate proporcionando métodos similares a los que usted describió. Además, estamos utilizando ISearch API, el proyecto de código abierto alojado en el código de Google y que proporciona una interfaz de creación de criterios muy conveniente para Hibernate y JPA.
puede crear una interfaz baseDAO y una clase de implementación baseDAO. Y cuando necesite un caso de uso específico con diferentes tipos de clase, puede crear el DAO de esa clase que hereda la clase baseDAO e implementar una interfaz adicional con las necesidades específicas de esa clase, como esta
IBaseDAO
public interface IBaseDAO<T> {
/**
* @Purpose :Save object of type T
* @param transientInstance
*/
public Object persist(final T transientInstance);
/**
* @Purpose :Delete object of type T
* @param persistentInstance
*/
public void remove(final T persistentInstance);
/**
* @Purpose :Update Object of type T
* @param detachedInstance
* @return
*/
public T merge(final T detachedInstance);
/**
* @Purpose :Find object by ''id'' of type T
* @param identifier
* @return
*/
public T findById(final Long identifier, Class<?> persistClass);
}
Clase BaseDAO
public class BaseDAO<T> implements IBaseDAO<T> {
@Autowired
private SessionFactory sessionFactory;
public Object persist(T entity) {
return this.getSession().save(entity);
}
@Override
public void remove(T persistentInstance) {
this.getSession().delete(persistentInstance);
}
@SuppressWarnings("unchecked")
@Override
public T merge(T detachedInstance) {
return (T) this.getSession().merge(detachedInstance);
}
@SuppressWarnings("unchecked")
@Override
public T findById(Long identifier, Class<?> persistClass) {
return (T) this.getSession().get(persistClass, identifier);
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public Session getSession() {
return getSessionFactory().getCurrentSession();
}
}
y una interfaz específica
public interface IUserDAO extends IBaseDAO<User> {
public User getUserById(long userId);
public User findUserByUsername(String username);
}
y clases como esta
@Repository("userDAO")
public class UserDAO extends BaseDAO<User> implements IUserDAO {
public User getUserById(long userId) {
return findById(userId, User.class);
}
@Override
public User findUserByUsername(String username) {
Criteria criteria = getSession().createCriteria(User.class);
criteria.add(Restrictions.eq("username", username));
return (User) criteria.uniqueResult();
}
}
puede usar DAO genérico como palanca para otras clases DAO específicas del dominio. Supongamos que tiene una clase de dominio de empleado como:
@Entity
@Table(name="employee")
public class Employee {
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Column(name="emp_name")
private String empName;
@Column(name="emp_designation")
private String empDesignation;
@Column(name="emp_salary")
private Float empSalary;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpDesignation() {
return empDesignation;
}
public void setEmpDesignation(String empDesignation) {
this.empDesignation = empDesignation;
}
public Float getEmpSalary() {
return empSalary;
}
public void setEmpSalary(Float empSalary) {
this.empSalary = empSalary;
}
}
entonces el DAO genérico requerido se vería así:
Interfaz DAO genérica:
public interface GenericRepositoryInterface<T> {
public T save(T emp);
public Boolean delete(T emp);
public T edit(T emp);
public T find(Long empId);
}
Implementación genérica de DAO:
@Repository
public class GenericRepositoryImplementation<T> implements GenericRepositoryInterface<T> {
protected EntityManager entityManager;
private Class<T> type;
public GenericRepositoryImplementation() {
// TODO Auto-generated constructor stub
}
public GenericRepositoryImplementation(Class<T> type) {
// TODO Auto-generated constructor stub
this.type = type;
}
public EntityManager getEntityManager() {
return entityManager;
}
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public T save(T emp) {
// TODO Auto-generated method stub
entityManager.persist(emp);
entityManager.flush();
return emp;
}
@Override
public Boolean delete(T emp) {
// TODO Auto-generated method stub
try {
entityManager.remove(emp);
} catch (Exception ex) {
return false;
}
return true;
}
@Override
public T edit(T emp) {
// TODO Auto-generated method stub
try{
return entityManager.merge(emp);
} catch(Exception ex) {
return null;
}
}
@Override
public T find(Long empId) {
// TODO Auto-generated method stub
return (T) entityManager.find(Employee.class, empId);
}
}
Esta clase DAO genérica necesita ser extendida por cada clase DAO específica del Dominio. La clase DAO específica del dominio puede incluso implementar otra interfaz para operaciones que no son comunes en general. Y prefiere enviar información de tipo usando constructor.
Spring Data JPA es un proyecto maravilloso que genera DAO para ti, ¡y más! Solo tiene que crear una interfaz (sin ninguna implementación):
interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {}
Esta interfaz (vía JpaRepository
heredado) le dará automáticamente:
PaymentMethod save(PaymentMethod entity);
Iterable<PaymentMethod> save(Iterable<? extends PaymentMethod> entities);
PaymentMethod findOne(Integer id);
boolean exists(Integer id);
Iterable<PaymentMethod> findAll();
long count();
void delete(Integer id);
void delete(PaymentMethod entity);
void delete(Iterable<? extends PaymentMethod> entities);
void deleteAll();
Iterable<PaymentMethod> findAll(Sort sort);
Page<PaymentMethod> findAll(Pageable pageable);
List<PaymentMethod> findAll();
List<PaymentMethod> findAll(Sort sort);
List<PaymentMethod> save(Iterable<? extends PaymentMethods> entities);
void flush();
PaymentMethod saveAndFlush(PaymentMethods entity);
void deleteInBatch(Iterable<PaymentMethods> entities);
La interfaz está fuertemente tipada (genéricos) y se implementa automáticamente para usted. Para cada entidad, todo lo que tiene que hacer es crear una interfaz que extienda JpaRepository<T,Integer extends Serializable>
.
¡Pero espera hay mas! Suponiendo que su PaymentMethod
tiene name
y campos validSince
como validSince
. Si agrega el siguiente método a su interfaz:
interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {
Page<PaymentMethod> findByNameLikeAndValidSinceGreaterThan(
String name, Date validSince, Pageable page
);
}
el marco analizará el nombre del método:
findBy
( Name like) And
( ValidSince greater than)
crea la consulta JPA QL, aplica paginación y clasificación ( Pageable page
) y Pageable page
por ti. No se requiere implementación:
paymentMethodsDao.findByNameLikeAndValidSinceGreaterThan(
"abc%",
new Date(),
new PageRequest(0, 20, Sort.Direction.DESC, "name"
);
Consulta resultante:
SELECT * //or COUNT, framework also returns the total number of records
FROM PaymentMethods
WHERE name LIKE "abc%"
AND validSince > ...
Y con paginación aplicada.
El único inconveniente es que el proyecto es bastante nuevo y es relativamente fácil golpear a los peros (pero se desarrolla muy activamente).