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);
}
}
Related
I have a project in spring boot and I'm using CrudRepository, but when I try to update, it doesn't do anything.
#Entity
public class PfmSelection implements Serializable{
#Id
private Integer releaseId;
private String preparedBy;
}
Repositiry
#Repository
public interface IPfmSelectionDao extends CrudRepository<PfmSelection, Integer> {
}
Service
public interface IPfmSelectionService {
public PfmSelection save(PfmSelection pfmSelection);
public PfmSelection findById(Integer id);
}
Service Impl
#Service
public class PfmSelectionService implements IPfmSelectionService {
#Autowired
private IPfmSelectionDao pfmSelectionDao;
#Override
#Transactional
public PfmSelection save(PfmSelection pfmSelection) {
return this.pfmSelectionDao.save(pfmSelection);
}
#Override
#Transactional(readOnly = true)
public PfmSelection findById(Integer id) {
return this.pfmSelectionDao.findById(id).orElse(null);
}
}
Service where I use the other Service
#Autowired
private IPfmSelectionService pfmSelectionService;
private void updatePfm(PushModel pushModel) {
PfmSelection pfm = this.pfmSelectionService.findById(167427);
pfm.setPreparedBy("Rodrige");
pfmSelectionService.save(pfm);
}
I don't receive any error in the console.
You need to take a few steps to know what the problem is
Take the return of pfmSelectionService.save(pfm) and print the saved instance returned like below:
private void updatePfm(PushModel pushModel) {
PfmSelection pfm = this.pfmSelectionService.findById(167427);
pfm.setPreparedBy("Rodrige");
PfmSelection pfm2 = pfmSelectionService.save(pfm);
System.out.println(pfm2.getPreparedBy());
}
Put logger/debugger inside the save method, before and after the save method and check for the entry/exit sop/logger statements in log/console like
#Override
#Transactional
public PfmSelection save(PfmSelection pfmSelection) {
System.out.println("Inside save method");
PfmSelection pfmSelectionSaved =
this.pfmSelectionDao.save(pfmSelection);
System.out.println("Exits save method");
return pfmSelectionSaved;
}
Check for any Aop around advice or any place where the exception is being caught but eaten/not thrown further.
Check if there is any update query fired in the logs at the time of save call.
Also check if the setter method pfm.setPreparedBy("Rodrige"); is Empty?
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 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();
}
Here is a simplified working code. There are a mapped superclass and two its subclasses (in real life superclass of course contains more fields)
Animal.java
#MappedSuperclass
#lombok.NoArgsConstructor
#lombok.RequiredArgsConstructor
public abstract class Animal {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#lombok.Getter
private Long id;
#lombok.Getter
#lombok.NonNull
private String name;
}
Cat.java
#Entity
#Table
#lombok.NoArgsConstructor
public class Cat extends Animal {
public Cat(Integer weight, String name) {
super(name);
this.weight = weight;
}
#lombok.Getter
private Integer weight;
}
Dog.java
#Entity
#Table
#lombok.NoArgsConstructor
public class Dog extends Animal {
public Dog(Integer age, String name) {
super(name);
this.age = age;
}
#lombok.Getter
private Integer age;
}
AnimalRepositoryImpl and AnimalRepository contain some shared code for Cat and Dog repositories.
AnimalRepository.java
#NoRepositoryBean
public interface AnimalRepository<T extends Animal> extends JpaRepository<T, Long> {
List<T> findAllByName(String name);
}
AnimalRepositoryImpl.java
public class AnimalRepositoryImpl<T extends Animal> {
#Autowired
AnimalRepository<T> animalRepository;
public List<T> findAllBySomeLogic() {
return animalRepository.findAll().stream().filter(animal -> !animal.getName().startsWith("Z")).collect(Collectors.toList());
}
}
Now I can add all CatRepositories and it still works (and works correctly).
CatRepository.java
#Transactional
public interface CatRepository extends AnimalRepository<Cat>, CatRepositoryCustom {
}
CatRepositoryCustom.java
public interface CatRepositoryCustom {
public List<Cat> findAllBySomeLogic();
}
CatRepositoryImpl.java
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
Here is a test class which still uses only cat repository.
AnimalRepositoryTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#ActiveProfiles(profiles = "test")
public class AnimalRepositoryTest {
#After
public void tearDown() {
catRepository.deleteAll();
}
#Autowired
private CatRepository catRepository;
#Test
public void shouldFindAllBySomeLogic() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllBySomeLogic();
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna", "Toby"));
}
#Test
public void shouldFindAllByName() {
// given
catRepository.save(Lists.newArrayList(new Cat(2000, "Luna"), new Cat(2500, "Zoe"), new Cat(1800, "Toby")));
// when
List<Cat> cats = catRepository.findAllByName("Luna");
// then
assertThat(cats.stream().map(c -> c.getName()).collect(Collectors.toList()), containsInAnyOrder("Luna"));
}
}
The way I've coded it was inspired mostly by this question (but my case is more complicated).
So... the main question. - How to add repositories for Dog (almost identical to Cat ones) and not to get something like NoUniqueBeanDefinitionException: No qualifying bean of type...? I've tried some variations with #Qualifier but seems it doesn't work in this case. Or maybe I'm doing it completely wrong.
I see at least one failure related to the generic definition of your classes. The class CatRepositoryImpl extends the classe AnimalRepositoryImpl without any generic Types. (See the following two code snippets of your post)
public class CatRepositoryImpl extends AnimalRepositoryImpl implements CatRepositoryCustom {
}
public class AnimalRepositoryImpl<T extends Animal> {
}
In my opinion it should look like.
public class CatRepositoryImpl extends AnimalRepositoryImpl<Cat> implements CatRepositoryCustom {
}
Beside that, I would avoid doing logic related things in a Repository class and move it to a Service level.
let imagine I have per entity a repository class (spring data jpa) for database access and a service class. The dependencies are managed by spring framework. Every service method does in most cases the same, so there is mainly code duplication:
public class NewsService {
#Inject
private NewsRepository newsRepository;
public void add(News news) {
// do some validation
newsRepository.save(news);
}
}
public class UserService {
#Inject
private UserRepository userRepository;
public void add(User user) {
// do some validation
userRepository.save(user);
}
}
Now i thought about creating an abstract class like this:
public abstract class AbstractService<T> {
private UnknownRepository unknownRepository;
public void add(T entity) {
// do some validation
unknownRepository.save(entity);
}
}
public class NewsService extends AbstractService<News> {
}
public class UserService extends AbstractService<User> {
}
My problem: How can i overwrite the repository used inside the abstract class based on my entities?
You can replace the UnknownRepository field with an abstract method and a type parameter:
// R is the type of the repository
public abstract class AbstractService<T,R extends BaseRepository> {
protected abstract R getRepository();
public void add(T entity) {
getRepository().save(entity);
}
}
And inject the specific repository to the implementations of this class:
public class NewsService extends AbstractService<News, NewsRepository> {
#Inject private NewsRepository newsRepository;
#Override
public NewsRepository getRepository() {
return newsRepository;
}
// the inherited add() method works now
}