I'm new to Java and I must make some DAO for my app. However, I don't want to make a DAO for each class (with interface) et override methods.
Is it possible to make a DAO extended by all the others, with methods working with all kind of Class ?
For example, a DAO that could handle class MyClass and class Foo with a single mehtod getList().
Thank you !
Not that gooed idea, in general, but...
If it is about low-level JDBC (no framework like Hibernate, Spring, etc.), then:
You can make an AbstractDAO class, then your other DAO-classes (UserDAO, ProductDAO, etc.), then you can make a CommonService class that has all those DAO-classes and provides the functions you need.
Example:
abstract class AbstractDAO {
private DataSource dataSource;
protected getDataSource() { // Inject it or hard-coded dataSource
return dataSource;
}
}
public class UserDAO extends AbstractDAO {
public User read(long id) {
// blablabla
return user;
}
public List<User> findAll() {
// blablabla
return users;
}
// and so on...
}
public class ProductDAO extends AbstractDAO {
public Product read(long id) {
// blablabla
return product;
}
public List<Product> findAll() {
// blablabla
return products;
}
// and so on...
}
Then other repositories, and then:
public class CommonService {
private final UserDAO userDAO = new UserDAO();
private final ProductDAO productDAO = new ProductDAO();
// other repositories
public User readUser(long id) {
return userDAO.read(id);
}
public Product readProduct(long id) {
return productDAO.read(id);
}
public List<User> findAllUsers() {
return userDAO.findAll();
}
public List<Product> findAllProducts() {
return productDAO.findAll();
}
}
And if you mean you want to make a generic repository (DAO), again not that good idea, because Spring has already made it in a quite good way (it calls JpaRepository, e.g. interface MyRepository extends JpaRepository<User, Long> { }):
Source: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.multiple-modules
But if you want, you can make such a mechanism too, based on something like this (but it will be a cumbersome work to make it work like it does in Spring, for instance; because they are a team of experts who worked day and night to realize such a tremendous project):
public abstract class Repo<T, K> {
public abstract T read(K id);
public abstract List<T> findAll();
}
or
public interface Repo<T, K> {
T read(K id);
List<T> findAll();
}
Related
I am currently working on a project where I have created the following custom Repository:
public interface ServiceRepository<T extends ServiceEntity> extends JpaRepository<T, UUID>, ServiceRepositoryCustom {
}
public interface ServiceRepositoryCustom {
List<ServiceEntity> findAllContainingName(String query);
}
#Repository("Repo")
public class ServiceRepositoryCustomImpl implements ServiceRepositoryCustom {
private final EntityManager em;
public ServiceRepositoryCustomImpl(EntityManager em) {
System.out.println("I got constructed");
this.em = em;
}
#Override
public List<ServiceEntity> findAllContainingName(String name) {
System.out.println("I got called with: " + name);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<ServiceEntity> cq = cb.createQuery(ServiceEntity.class);
Root<ServiceEntity> serviceEntity = cq.from(ServiceEntity.class);
List<Predicate> predicates = new ArrayList<>();
if(name != null) {
// predicates.add(cb.equal(serviceEntity.get("name"), name));
predicates.add(cb.like(serviceEntity.get("name"), name + "%"));
}
cq.where(predicates.toArray(predicates.toArray(new Predicate[0])));
return em.createQuery(cq).getResultList();
}
}
The print statement "I got called with: " never gets called. So for whatever reason Spring Boot is not running the method through my custom implementation.
Any suggestions? Any help is much appreciated
Edit:
Here is the code that injects and uses the Repository in question
#Repository
public interface PineappleServiceRepository extends ServiceRepository<PineappleServiceEntity> {
}
#Component("Registry")
#DependsOn({"Context", "Repo"})
public class Registry {
private final List<ServiceRepository<? extends ServiceEntity>> serviceRepositories = new ArrayList<>();
public Registry(PineappleServiceRepository pineappleServiceRepository) {
this.serviceRepositories.add(pineappleServiceRepository);
}
}
Edit 2:
The code prints "I got constructed"
Edit 3:
Class where findAllContainingName is called
#RestController
#RequestMapping("/test")
#DependsOn("Registry")
public class ServiceController {
private final Registry registry;
public ServiceController(#NotNull Registry registry) {
this.registry = registry;
}
#GetMapping("")
List<ServiceEntity> all(#RequestParam("q") String query) {
return getAllServices(query);
}
private #NotNull List<ServiceEntity> getAllServices(String query) {
List<ServiceEntity> response = new ArrayList<>();
for(ServiceRepository<? extends ServiceEntity> repo: this.registry.getServiceRepositories()){
response.addAll(repo.findAllContainingName(query));
}
return response;
}
}
Edit 4:
Here the entities:
#Entity
#Table(name = "services")
public abstract class ServiceEntity {
protected #Id
UUID id = UUID.randomUUID();
protected String name;
// Constructor + Getters and Setters
}
#Entity
public class PineappleServiceEntity extends ServiceEntity {
// Additional Properties, matching Constructors, Getters and Setters
}
So I was able to reproduce your problem and fix it. Issue with your code is that your PineappleServiceRepository is not extending ServiceRepositoryCustom directly. It seems your repository needs to implement it directly if you are accessing custom repository methods from that repository. I got that idea from this post.
So to fix your issue, either remove PineappleServiceRepository(as you don't have any properties in PineappleEntity) and use ServiceRepository to call that custom method or make PineappleServiceRepository extend ServiceRepositoryCustom.
I have pushed changes to GitHub with fix. You can take a look. If you want to keep PineappleServiceRepository and access custom method using this repository, let me know, I can update code.
I have two JPA entites like
#Entity
#Table(name = "A")
public class A {...}
#Entity
#Table(name = "B")
public class B {...}
And services like
#Service
public class AServiceImpl implements AService {
#Autowired
private ARepository aRepository;
public void create() {...}
public void update(long id) {...}
public void doA() {...}
}
#Service
public class BServiceImpl implements BService {
#Autowired
private BRepository bRepository;
public void create() {...}
public void update(long id) {...}
public void doB() {...}
}
So, in methods create and update I have code what differs only by repository. I would like to store crud methods in some other, basic service, but do not really understand how could I manage repository inheritance.
Any help, please.
IMP: This code is compiling, I havent tested it.
I think you can use Genarics over here.
public class RepositoryService <R extends JpaRepository<T, Long>, T> {
#Autowired R repository;
public void insert(T object) {
repository.save(object);
}
}
How to effectively use these reporitories, that details you can refer to this baeldung article - https://www.baeldung.com/spring-autowire-generics
Update: This is an alternate approach you can try out
public abstract class RepositoryService <T>{
abstract Class getRepositoryClass();
Map<Class, GenericRepository> repositories;
#Autowired
List<GenericRepository> repositoriesList;
#PostConstruct
public void setupMap() {
// Convert List into Map, with the class of Objest to be saved.
}
public void insert(T object) {
repositories.get(object.getClass()).save(object);
}
}
What I would like to achieve is to use interfaces for domain classes and generic types for service layer and be able to change the implementation of the persistence layer from current which is MongoDb to e.g. JPA. Interfaces for domain classes are necessary because of e.g different annotations for JPA and MongoDB (#Entity and #Document).
Let's look at the structure of the following demo project:
For each element of the domain model there can be three interfaces, let's explain it using the user package:
User - representation of domain object
UserDao - providing persistence layer methods
UserService - providing business logic methods
Here are interfaces for each of them:
public interface User {
String getId();
String getFirstName();
String getLastName();
List<Consent> getConsents();
Boolean getBlocked();
}
public interface UserDao <UserType extends User> {
UserType save(UserType user);
Optional<UserType> getById(String userId);
}
public interface UserService <UserType extends User> {
UserType create(String firstName, String lastName);
void addConsent(UserType user, ConsentType consentType);
}
As I mentioned earlier, current implementation of those interfaces is related to Mongo DB:
#Getter
#Setter
#Document(collection = "user")
public class MongoUser extends AbstractMongoCollection implements User {
private String firstName;
private String lastName;
private List<PojoConsent> consents;
private Boolean blocked;
void addConsent(PojoConsent consent) {
if(consents == null) {
consents = new ArrayList<>();
}
consents.add(consent);
}
#Override
public List<Consent> getConsents() {
return new ArrayList<>(consents);
}
}
#Component
public class MongoUserDao implements UserDao<MongoUser> {
private MongoUserRepository mongoUserRepository;
#Autowired
public MongoUserDao(MongoUserRepository mongoUserRepository) {
this.mongoUserRepository = mongoUserRepository;
}
#Override
public MongoUser save(MongoUser user) {
return mongoUserRepository.save(user);
}
#Override
public Optional<MongoUser> getById(String userId) {
return mongoUserRepository.findByIdAndDeletedIsFalse(userId);
}
}
#Component
public class MongoUserService implements UserService<MongoUser> {
private UserDao<MongoUser> userDao;
#Autowired
public MongoUserService(UserDao<MongoUser> userDao) {
this.userDao = userDao;
}
#Override
public MongoUser create(String firstName, String lastName) {
MongoUser user = new MongoUser();
user.setBlocked(false);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setDeleted(false);
return userDao.save(user);
}
#Override
public void addConsent(MongoUser user, ConsentType consentType) {
PojoConsent pojoConsent = new PojoConsent();
pojoConsent.setActive(true);
pojoConsent.setType(consentType);
pojoConsent.setDate(LocalDateTime.now());
user.addConsent(pojoConsent);
userDao.save(user);
}
}
Ok, so what is the problem ? The problem occurs when I inject beans of type UserDao and UserService in other beans (as it happens in Spring Framework), like EntryPoint in this example (I'm aware of that there should be no logic in spring controller, but this is just an example):
#RestController
#RequestMapping("/api")
public class EntryPoint {
#Autowired
private ConversationService conversationService;
#Autowired
private UserDao userDao;
#PostMapping("/create/{userId}")
public ResponseEntity<String> createConversation(#PathVariable("userId") String userId) {
Optional<User> optionalUser = userDao.getById(userId);
if(optionalUser.isPresent()) {
User user = optionalUser.get();
Conversation conversation = conversationService.create(user, "default");
return ResponseEntity.ok(conversation.getId());
}
return ResponseEntity.notFound().build();
}
}
Interfaces ConversationService and UserDao have a generic type so warnings appear:
I don't want to give up generic types but on the other hand I'm aware that injecting without generic types will cause warnings which does not comply with clean code principles. It is true that this design will work despite warnings. I don't want to change implementation of the EntryPoint when I change persistence layer from MongoDb to JPA, I just want to provide new implementation for domain interfaces (User, UserDao, UserService etc.)
How to reconcile the interface issue for domain domain classes and injecting without generic type ?
I'm writing Java program, which interacts with Db via Hibernate.
All my persistent classes extend from common abstract class Entity which implements interface IEntity. For example:
public interface IEntity {
long getId();
void setId(long id);
}
public abstract class Entity implements IEntity {
private long id;
//get + set id
}
public class User extends Entity {
private string name;
//get + set name
}
public class Item extends Entity {
private string description;
//get + set description
}
For operations with Db I created repository classes which extend from Repository<T extends IEntity> with standard CRUD methods for all entities and this class implements interface IRepository<T extends IEntity>:
public interface IRepository<T extends IEntity> {
void create(T object) throws JDBCException;
//other CRUD operations
}
public abstract class Repository<T extends IEntity> implements IRepository<T> {
private final Class<T> entityClass;
protected final EntityManager entityManager;
public Repository(Class<T> entityClass, EntityManager entityManager) {
this.entityClass = entityClass;
this.entityManager = entityManager;
}
#Override
public void create(T object) throws JDBCException {
entityManager.getTransaction().begin();
entityManager.persist(object);
entityManager.getTransaction().commit();
}
//other CRUD operations implementation
}
public class UserRepository extends Repository<User> {
public UserRepository (EntityManager entityManager) {
super(AmountUnit.class, entityManager);
}
}
public class ItemRepository extends Repository<Item> {
public ItemRepository (EntityManager entityManager) {
super(AmountUnit.class, entityManager);
}
}
This structure worked well until I decided to create method to obtain specific repository by its entity class.
I see this method as something like this:
public <T extends IEntity, U extends IRepository<T>> U getByType(T object) {
// code here
}
Let's say, that class User extends Entity and have repository class UserRepository extends Repository<User>
I'm expecting, that this method should return RepositoryforUser object`.
From my point of view this can be achieved in two ways:
Elegant. Create method for IRepository - Class<T> getEntityClass
and then compare classes of input and result of getEntityClass
Stupid. Make many if/else statements inside this method and return repository. if(object instanceof A) return ARepository
public class Storage {
private IRepository<? extends IEntity>[] repositories;
public <T extends IEntity, U extends IRepository<T>> U getByTypeVar1(T object) {
for (IRepository<?> repo : repositories) {
if (object instanceof repo.getEntityClass ()) // cannot resolve getEntityClass
return repo;
}
}
public <T extends IEntity, U extends IRepository<T>> U getByTypeVar2(T object) {
if (object instanceof UserRepository.getEntityClass ())
return UserRepository; //incompatible type
//more if else here
}
}
But both of these implementation are failed to compile. May be you have any ideas how to write this method correctly
You can implement the getByType method like this (I changed the parameter type):
private List<IRepository<? extends IEntity>> repositories;
#SuppressWarnings("unchecked")
public <E extends IEntity> IRepository<E> getByType(Class<E> entityClass) {
for (IRepository<?> repository : repositories) {
if (repository.getEntityClass().equals(entityClass)) {
return (IRepository<E>) repository;
}
}
throw new IllegalArgumentException(
"No repository for entity class " + entityClass.getName());
}
When you post your code that failed to compile, we can figure out where the problem was.
Update (code comments)
You should add the getEntityClass() method to IRepository.
To make the code less complicated, you can replace:
<T extends IEntity, U extends IRepository<T>> U getByType()
with
<T extends IEntity> IRepository<T> getByType getByType()
Using instanceof in
object instanceof repo.getEntityClass ()
can be problematic, since you can have entity hierarchies and you can get a wrong (subclass) repository for an object. If you don't know a class of the object, you can get it by (the object can be a Hibernate proxy):
org.hibernate.Hibernate.unproxy(object).getClass()
and then compare the classes by repository.getEntityClass().equals(entityClass).
I'm trying to write simple DAO that will create entity objects based on their type stored in String field. How to return type that is changed dynamicly?
Method findById() of UserDAO class should return User class object. Same method for ProductDAO should return Product.
I don't want to implement findById in every class that extends DAO, it should be done automatically.
Example code:
class DAO {
protected String entityClass = "";
public (???) findById(int id) {
// some DB query
return (???)EntityFromDatabase; // how to do this?
}
}
class UserDAO extends DAO {
protected String entityClass = "User";
}
class ProductDAO extends DAO {
protected String entityClass = "Product";
}
class User extends Entity {
public int id;
public String name;
}
Modify it to
class DAO<T> {
// protected String entityClass = "";
public T findById(int id) {
return (T)EntityFromDatabase; // how to do this?
}
}
class UserDAO extends DAO<User> {
//protected String entityClass = "User";
}
class ProductDAO extends DAO<Product> {
//protected String entityClass = "Product";
}
class User extends Entity {
public int id;
public String name;
}
Use Generics in java. Find an example here.
public interface GenericDAO<T,PK extends Serializable> {
PK create(T entity);
T read(PK id);
void update(T entity);
void delete(T entity);
}
public class GenericDAOImpl<T,PK extends Serializable> implements GenericDAO<T,PK>{
private Class<T> entityType;
public GenericDAOImpl(Class<T> entityType){
this.entityType = entityType;
}
//Other impl methods here...
}
Firstly, instead of using the String, use the class. Next up, use an entityManager (see docs)
class DAO<T> {
private Class<T> entityClass;
// How you get one of these depends on the framework.
private EntityManager entityManager;
public T findById(int id) {
return em.find(entityClass, id);
}
}
Now you can use a different DAO dependent on the type e.g.
DAO<User> userDAO = new DAO<User>();
DAO<Product> userDAO = new DAO<Product>();
I strongly recommend you this article, Don't Repeat the DAO. And I must say, you are not having a bad idea.