I have many services that have repeated code and I want to know how to implement a generic service so all my services can extend it.
Service interfaces example (Repeated code):
#Service
public interface IUserService{
List<User> findAll();
User save(User entity);
User findById(long id);
void delete(User entity);
void deleteById(long id);
long count();
}
#Service
public interface IEventService{
List<Event> findAll();
Event save(Event entity);
Event findById(long id);
void delete(Event entity);
void deleteById(long id);
long count();
}
And their implementations (Now, I have the same code in all the implementations):
#Service
public class EventService implements IEventService{
#Autowired
private IEventDao dao;
#Override
public List<Event> findAll() {
return dao.findAll();
}
#Override
public Event save(Event entity) {
return dao.save(entity);
}
Other CRUD methods...
}
#Service
public class UserService implements IUserService{
#Autowired
private IUserDao dao;
#Override
public List<User> findAll() {
return dao.findAll();
}
#Override
public User save(User entity) {
return dao.save(entity);
}
Other CRUD methods...
}
This is fairly straightforward using Java generics. You can replace the actual class User, Event, etc. with a type parameter.
public interface IGenericService<T> {
List<T> findAll();
T save(T entity);
T findById(long id);
void delete(T entity);
void deleteById(long id);
long count();
}
Then do the same for the implementation:
public class GenericService<T> implements IGenericService<T> {
// The DAO class will also need to be generic,
// so that it can use the right class types
#Autowired
private IDao<T> dao;
#Override
public List<T> findAll() {
return dao.findAll();
}
#Override
public T save(T entity) {
return dao.save(entity);
}
// Other CRUD methods...
}
Even further, you can also create your actual services as:
#Service
class UserService extends GenericService<User> { }
#Service
class EventService extends GenericService<Event> { }
Here's a good tutorial from the Java documentation: Learning the Java Language: Generics
Another one with good examples: The Basics of Java Generics
Related
Knowing that there are some frameworks to do it in real-world situation. As I learning from scratch, I wonder is that possible to create pure MongoDB DAO as a layer on top of the Service class to do CRUD operation that also facilitate other DAO use it?
For example, below is my Generic DAO class to operate CRUD process.
public interface IGenericDAO<T> {
public T create(T t);
public T update(T t);
public T get(Object id);
public void delete(Object id);
public List<T> listAll();
public Long count();
}
Then, DAO class should implements its operations
public class UserDAO implements IGenericDAO<User> {
MongoDatabase database = dBUtils.getMongoDB();
MongoCollection <Document> userTbl = database.getCollection("User");
public UserDAO() {
super();
}
#Override
public User create(User user) {
return user;
}
// other CURD below
}
User Service class
public class UserService {
private UserDAO userDAO;
public UserService() {
}
public void listUser() {
// need to test
List<User> listUsers = userDAO.listAll();
}
public void create(User user) {
// this what I want to see. user is saved to db here
try {
MongoDatabase database = dBUtils.getMongoDB();
assert mdb != null;
// create or get collection
MongoCollection<User> userTbl = database.getCollection("Users", User.class);
User userDoc = new User();
userTbl.insertOne(userDoc);
} catch (Exception e) {
e.printStackTrace();
}
}
Having said that, I want to put MongoDB "layer" between the DAO and Service class to do the CRUD operation. I wonder is it necessary, otherwise, how to do it to help the UserService with UserDAO class?
I want to use method a from UserRepository in UserService, but I'm getting jpaRepository instead my custom implementation, how should I write classes to get it?
Repository:
#Repository
public interface UserRepository<UserEntity extends EntityInterface,Long> extends JpaRepository<UserEntity,Long> {
Optional<UserEntity> findUserByLogin(String login);
}
CrudAbstractService with generics method:
public abstract class CrudAbstractService<ENTITY extends EntityInterface, DTO extends DTOInterface> {
protected final JpaRepository<ENTITY, Long> jpaRepository;
protected final Validator<DTO> validator;
protected final MapperInterface<ENTITY, DTO> mapper;
private Class<ENTITY> entityClazz;
public CrudAbstractService(JpaRepository<ENTITY, Long> jpaRepository,
Validator<DTO> validator, MapperInterface<ENTITY, DTO> mapper) {
this.jpaRepository = jpaRepository;
this.validator = validator;
this.mapper = mapper;
}
public Iterable<DTO> findAll() {
List<ENTITY> allEntities = jpaRepository.findAll();
if (allEntities == null) {
throw new EntityNotFound(entityClazz);
}
List<DTO> mappedDTOs = mapper.toDTOs(allEntities);
return mappedDTOs;
}
public void delete(DTO dto) {
validator.validate(dto);
ENTITY entity = mapper.toEntity(dto);
jpaRepository.delete(entity);
}
public DTO save(DTO dto) {
validator.validate(dto);
ENTITY entity = mapper.toEntity(dto);
ENTITY save = jpaRepository.save(entity);
if (save == null) {
throw new EntityNotFound(entityClazz);
}
DTO mappedDTO = mapper.toDTO(save);
return mappedDTO;
}
}
Implementation of CrudUserService, there I want to inject UserRepository instead of JpaRepository:
#Service
public class UserService extends CrudAbstractService<UserEntity,UserDTO> {
private MapperInterface<LectureEntity,LectureDTO> lectureMapper;
public UserService(UserRepository<UserEntity, Long> jpaRepository,
Validator<UserDTO> validator, MapperInterface<UserEntity, UserDTO> mapper,
MapperInterface<LectureEntity,LectureDTO> lectureMapper) {
super(jpaRepository, validator, mapper);
this.lectureMapper = lectureMapper;
}
public UserDTO findUserByLogin(String login) {
if (login == null) {
throw new UserNotFoundException();
}
//Here i want use UserRepository method instead of JpaRepository.
Optional<UserEntity> userByLogin = jpaRepository.findUserByLogin(login);
UserEntity userEntity = userByLogin.orElseThrow(UserNotFoundException::new);
List<LectureEntity> reservations = userEntity.getReservations();
List<LectureDTO> lectureDTOS = lectureMapper.toDTOs(reservations);
UserDTO userDTO = mapper.toDTO(userEntity);
userDTO.setLectures(lectureDTOS);
return userDTO;
}
}
I think you don't need to make you repository interface generic.
So, replace this:
#Repository
public interface UserRepository<UserEntity extends EntityInterface,Long> extends JpaRepository<UserEntity,Long> {
Optional<UserEntity> findUserByLogin(String login);
}
with this:
#Repository
public interface UserRepository extends JpaRepository<UserEntity,Long> {
Optional<UserEntity> findUserByLogin(String login);
}
And use it in your service:
#Service
public class UserService extends CrudAbstractService<UserEntity,UserDTO> {
private MapperInterface<LectureEntity,LectureDTO> lectureMapper;
public UserService(UserRepository jpaRepository,
Validator<UserDTO> validator, MapperInterface<UserEntity, UserDTO> mapper,
MapperInterface<LectureEntity,LectureDTO> lectureMapper) {
super(jpaRepository, validator, mapper);
this.lectureMapper = lectureMapper;
}
}
If you need to map your entities to DTOs then you can try to use JPA projections
Regarding throwing an exception in findAll() - in my opinion, it's not a good idea. You should probably return just empty list and let the clients of your class decide what to do in case of missing entities.
Also in your case I would try to avoid using abstract classes and inheritance and use composition instead.
Inheritance versus composition: How to choose and Why should I prefer composition over inheritance?
i have created a Base entity interface with getId() method and implemented into
entities for doing crud operations on all entity save is works and
delete,update also work but retriving by id doesnt work i am not sure its
possible or not if possible then suggest or any other way.
this is base interface for all entities and getId() overrides in each entity
public interface DemoEntity extends Serializable {
public long getId();
}
this is an user entity
public class User implements Serializable,DemoEntity {
getter/setters
}
public class Subject implements Serializable,DemoEntity {
getter/setters
}
//this is modelmanager class for doing crud operations
public class ModelManager {
#Autowired
#PersistenceContext
private EntityManager em;
#Transactional
public void save(DemoEntity entity) {
em.persist(entity);
}
#Transactional
public DemoEntity getEntityById(long id) {
DemoEntity de=em.find(DemoEntity.class, id);
return de;
}
}
#Autowired <-- is not necessary
#PersistenceContext
private EntityManager em;
Your method would looks like better
#Transactional
public DemoEntity getEntityById(long id) {
return em.find(DemoEntity.class, id);
}
and on the top of the class you need to put an annotation like #Component #Repository
And finally you did not say what kind of error you got
I have an abstract DAO:
public abstract class AbstractJpaDAO<T extends Serializable> implements I_AbstractJpaDAO<T> {
private Class<T> clazz;
#PersistenceContext
protected EntityManager entityManager;
public final void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
#Override
public T findOne(final long id) {
return entityManager.find(clazz, id);
}
#Override
public List<T> findAll() {
return entityManager.createQuery("from " + clazz.getName()).getResultList();
}
#Override
public void create(final T entity) {
entityManager.persist(entity);
}
#Override
public T update(final T entity) {
return entityManager.merge(entity);
}
#Override
public void delete(final T entity) {
entityManager.remove(entity);
}
#Override
public void deleteById(final long entityId) {
final T entity = findOne(entityId);
delete(entity);
}
}
I then extend this DAO in each DAO implementation (code not included) but header something like:
public class UserDAOImpl extends AbstractJpaDAO <User> implements UserDAO {
.....
With each entity, I work with a base interface type, for this example,let's call it UserDAO, and have an associated implementation, let's call it, UserDAOIMPL. To avoid having to define the same methods each Interface to each DAO. As in this longwinded example, i.e :
public interface UserDAO {
User findOne(long id);
List<User> findAll();
void create(User user);
User update(User user);
void delete(User user);
void deleteById(long userID);
User findUserByUserName(String name);
EntityManager returnEntityManager();
}
I would like to instead create a base interface, that all DAOs can extend.
public interface I_AbstractJpaDAO<T> {
.....
}
and then use this in each DAO interface.
public interface userDAO extends I_AbstractJpaDAO<T> { .....
However, I'm getting problems with "both methods have erasure, but neither overrides the other". Something to do with the Serialization thing I suspect. Can anyone help me please?
It means that, your base interface and abstract interface have the methods with the same signature and different return type.
Try this:
public interface userDAO extends I_AbstractJpaDAO<User> { .....
So I have such classes
BaseDao
public interface BaseDao<T> {
T save(T entity);
}
UserDao
public interface UserDao extends BaseDao<User> {
User getUserByUserName(String name);
}
GenericDao
public abstract class GenericDao implements BaseDao {
#Autowired
protected SessionFactory sessionFactory;
private Class<?> getEntityClass() {
return ((Class) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0]);
}
public T save(T entity) {
Integer id = (Integer) sessionFactory.getCurrentSession().save(entity);
return (T) sessionFactory.getCurrentSession().get(getEntityClass(), id);
}
}
UserDaoImpl
#Repository
public class PgUserDaoImpl extends GenericDao<User> implements UserDao {
public User getUserByUserName(String name) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
User.class);
criteria.add(Restrictions.eq("name", name));
return (User) criteria.uniqueResult();
}
}
GenericService
public class GenericService<T extends GenericDao<E>, E> {
protected T dao;
public GenericService(T dao) {
setDao(dao);
}
#Transactional
public E save(E entity) {
return dao.save(entity);
}
}
UserServiceImpl
#Service("userServiceImpl")
public class UserServiceImpl extends GenericService<PgUserDaoImpl, User>
implements UserService {
#Autowired
public UserServiceImpl(PgUserDaoImpl dao) {
super(dao);
}
#Autowired
private UserAssebmler assebmler;
#Transactional
#Override
public UserDetails loadUserByUsername(String name)
throws UsernameNotFoundException {
.....
}
}
So I write test contoller to save user, but I always get Exception
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
The contoller
#
Controller
#RequestMapping(value = "/test")
public class TestController {
#Autowired
private UserServiceImpl userService;
#RequestMapping(value = "save", method = RequestMethod.GET)
public String test() {
User user = new User();
user.setName("admin");
user.setPassword("21232f297a57a5a743894a0e4a801fc3");
user.setRole(UserRole.ADMIN);
userService.save(user);
return "home";
}
}
Anybody know hot to fix that? Thanks :)
I've solved my question by extending GenericDao class from HibernateDaoSupport class.