Hibernate: CRUD Generic DAO
В моем веб-приложении много сервисных таблиц/сущностей, таких как payment_methods
, tax_codes
, province_codes
и т.д.
Каждый раз, когда я добавляю новый объект, я должен написать DAO. Дело в том, что, в основном, они все одинаковые, но единственное отличие состоит в классе сущности.
Я знаю, что инструменты Hibernate могут автоматически генерировать код для меня, но я не могу использовать их сейчас (не спрашивайте почему), поэтому я думаю о Generic DAO. Там много литературы об этом, но я не могу скомпоновать фрагменты и заставить его работать с Spring.
Все о дженериках, я думаю, будет иметь четыре основных метода:
-
listAll
-
saveOrUpdate
-
deleteById
-
getById
и все.
Вопрос:
Какая лучшая практика для повторного изобретения колеса? Разве что-то не готово к использованию?
Ответы
Ответ 1
здесь моя
@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
и затем вы можете получить доступ к этому слову в сервисе:
@Autowired
private Dao dao;
@Transactional(readOnly = true)
public List<MyEntity> getAll() {
return dao.getAll(MyEntity.class);
}
Ответ 2
Spring Данные JPA - прекрасный проект, который генерирует DAO для вас и многое другое! Вам нужно создать интерфейс (без какой-либо реализации):
interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {}
Этот интерфейс (через унаследованный JpaRepository
) автоматически даст вам:
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);
Интерфейс строго типизирован (generics) и автоматически реализован для вас. Для каждого объекта все, что вам нужно сделать, это создать интерфейс, расширяющий JpaRepository<T,Integer extends Serializable>
.
Но подождите, там еще! Предполагая, что ваш PaymentMethod
имеет постоянные поля name
и validSince
. Если вы добавили к вашему интерфейсу следующий метод:
interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {
Page<PaymentMethod> findByNameLikeAndValidSinceGreaterThan(
String name, Date validSince, Pageable page
);
}
структура проанализирует имя метода:
findBy
(Name like) And
(ValidSince больше)
создайте запрос JPA QL, примените пейджинг и сортировку (Pageable page
) и запустите его для вас. Не требуется внедрение:
paymentMethodsDao.findByNameLikeAndValidSinceGreaterThan(
"abc%",
new Date(),
new PageRequest(0, 20, Sort.Direction.DESC, "name"
);
Результирующий запрос:
SELECT * //or COUNT, framework also returns the total number of records
FROM PaymentMethods
WHERE name LIKE "abc%"
AND validSince > ...
И при использовании пейджинга.
Единственным недостатком является то, что проект довольно новый, и относительно легко ударить нос (но он очень активно развивается).
Ответ 3
Не записывайте конкретные dao для каждого объекта. Вы можете реализовать один общий DAO, который выполняет 90% работы для всех объектов, которые вам нужны. Вы можете продлить его в случаях, когда требуется конкретное обращение с определенными объектами.
В проекте, над которым я сейчас работаю, есть такой DAO, который обертывает сеанс Hibernate, предоставляя методы, аналогичные тем, которые вы описали. Кроме того, мы используем ISearch API - проект с открытым исходным кодом, размещенный в коде google, и обеспечивающий очень удобный интерфейс построения критериев для Hibernate и JPA.
Ответ 4
вы можете использовать Generic DAO в качестве рычага для других классов DAO для домена. Предположим, что у вас есть класс Employee Domain:
@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;
}
}
тогда требуемый общий DAO будет выглядеть примерно так:
Общий интерфейс DAO:
public interface GenericRepositoryInterface<T> {
public T save(T emp);
public Boolean delete(T emp);
public T edit(T emp);
public T find(Long empId);
}
Общая реализация 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);
}
}
Этот общий класс DAO затем должен быть расширен каждым классом DAO для каждого домена. Доменный класс DAO может даже реализовать другой интерфейс для операций, которые не являются общими в общем. И предпочитайте передавать информацию типа с помощью конструктора. Для полного объяснения и загружаемого примера вы можете посмотреть на эту ссылку:
http://frameworkonly.com/generic-dao-hibernate-jpa-spring-example/
Ответ 5
вы можете создать интерфейс baseDAO и класс реализации baseDAO.
И когда вам нужен конкретный вариант использования с разными типами классов, вы можете просто создать класс DAO, который наследует класс baseDAO и реализует дополнительный интерфейс с такими специфическими для класса требованиями, как этот
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);
}
Класс 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();
}
}
и конкретный интерфейс
public interface IUserDAO extends IBaseDAO<User> {
public User getUserById(long userId);
public User findUserByUsername(String username);
}
и классы, подобные этому
@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();
}
}