I'm building a small application using Spring and Spring Data JPA and I need to use the CrudRepository methods in the service layer, so I made 2 classes: GenericService and GenericServiceImpl. But I don't know if this is the right or even the best approach.
Here is an example:
POJO:
#Entity
public class User {
#Id
private Long id;
private String username;
}
DAO:
public interface UserDAO extends CrudRepository<User, Long> {
User findOneByUsername(String username);
}
Generic service
public interface GenericService<T, ID extends Serializable> {
<S extends T> S save(S entity);
}
Service
public interface UserService extends GenericService<User, Long> {
User findOneByUsername(String username);
}
Generic service impl.
public class GenericServiceImpl<T, ID extends Serializable> implements GenericService<T, ID> {
#Autowired
private CrudRepository<T, ID> repository;
#Override
public <S extends T> S save(S entity) {
return repository.save(entity);
}
}
Service Impl.
#Service
#Transactional
public class UserServiceImpl extends GenericServiceImpl<User, Long> implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public User findOneByUsername(String username) {
userDAO.findOneByUsername(username);
}
}
Yes, you're providing your own impl that's may do custom things while still reusing the Derived queries from Spring JPA's CrudRepository. I've seen that a lot. :) We need to do that if we want to do our own computation before calling the Derived methods. After all, that computation may very well be part of a repository, so it doesn't make sense to put that logic in the service. But in your scenario, if there are no such custom computations, then this indirection isn't needed. As a commenter mentioned, you should directly use the UserDao interface. :)
Related
public interface AccountRepository extends CrudRepository<AccountDBModel, Long> {
#Modifying
#Query(value = PortfolioQuery.ACCOUNT_INSERT)
void insert(#Param("exchangeId") Long exchangeId, #Param("name") String name, #Param("siteAccount") String siteAccount,
#Param("memo") String memo, #Param("createdAt") Long createdAt, #Param("updatedAt") Long updatedAt,
#Param("isActive") Boolean isActive);
#Modifying
#Query(value = PortfolioQuery.ACCOUNT_UPDATE)
void update(#Param("id") Long id, #Param("exchangeId") Long exchangeId, #Param("name") String name,
#Param("siteAccount") String siteAccount, #Param("memo") String memo, #Param("updatedAt") Long updatedAt,
#Param("isActive") Boolean isActive);
#Query
Optional<AccountDBModel> findByName(#Param("name") String name);
}
#Service
public class AccountService {
private final AccountRepository repository;
#Autowired
public AccountService(AccountRepository repository) {
this.repository = repository;
}
public void postAccount(AccountBaseModel baseModel) throws Exception {
Long now = System.currentTimeMillis();
this.repository.insert(baseModel.getExchangeId(), baseModel.getName(), baseModel.getSiteAccount(),
baseModel.getMemo(), now, now, baseModel.getIsActive());
}
}
#SpringBootTest
class WaveBackofficeApiApplicationTests {
#Autowired
private ApplicationContext applicationContext;
#Test
public void contextLoads() throws Exception {
if (applicationContext != null) {
String[] beans = applicationContext.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println("bean : " + bean);
}
}
}
}
As you can see in AccountRepository interface I didn't use #Repository in AccountRepository interface.
But why is it registered as a bean in Spring Container?
There are no other class like AppConfig.
The interface itself is not registered as a bean. spring framework provides existing implementation of a repository bean (default impl is the class SimpleJpaRepository), which gets injected based on the specifications you provide in your interface. This specific class has the #Repository annotation and will be picked up by spring as a bean.
A simple overview:
#Repository
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
// code
}
public interface MyRepository extends CrudRepository<T, ID> {}
#Service
public MyService() {
#Autowired private MyRepository myRepository;
}
In the example above, our own repository interface extends CrudRepository, which has an implementation class named SimpleJpaRepository (provided in the framework), and SimpleJpaRepository is registered as a bean. In MyService, we just tell that we want a bean of type MyRepository, and Spring will inject an instance of SimpleJpaRepository.
You created interface called AccountRepository and extended (thus inherited) CrudRepository.
Now just do Ctrl + Left mouse click on CrudRepository, you will end up in it:
#NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAllById(Iterable<? extends ID> ids);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
Intellij actually gives you oportunity to find the implementations of all those methods with arrow down mark on the left side.
So there is a huge class called SimpleJpaRepository that has all the implementations, the actual code.
AND THE THING IS...
SimpleJpaRepository.class does have #Repository in it:
#Repository
#Transactional(
readOnly = true
)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
I'm using Spring to communicate with mysql db.
I've got an Entity class and an interface that extends CrudRepository.
Everything works great - read/write etc.
I want to 'extend' the findAll methods. I want to manipulate the received data from findAll before returning it.
user class:
#Entity
#Table(name = "user")
public class User
{
private String name;
private String age;
private String type;
getters/setters
}
repo:
#Repository
public interface UserRepo extends CrudRepository<User, Long> {
List<User> findAll();
Map<String, String> afterManipulatedFindAllData();
}
I want that afterManipulatedFindAllData() will manipulate findAll data as I like.
Is that possible?
added
after reviewing #BoristheSpider link:
interface UserRepository {
Map<String, String> afterManipulatedFindAllData();
}
class UserRepositoryImpl implements UserRepository{
public Map<String, String> afterManipulatedFindAllData() {
////how this method receive the 'findAll' data?//////
}
}
public interface UserRepo extends CrudRepository<User, Long>, UserRepository
{
List<User> findAll();
Map<String, String> afterManipulatedFindAllData();
}
Thanks a lot,
Avi
You can always define new methods in the repository by specifying a JPA query for them:
#Component
public interface UsersRepository extends JpaRepository<User, UUID> {
public List<User> findByEntityStatus(EntityStatus status);
#Query("from User user left outer join fetch user.areas where user.code = :code")
public User findByCode(#Param("code") String code);
}
Maybe this could work for you?
I'm trying to follow code reusing best practices.
I have generic DAO interface with some common methods:
public interface DaoInterface<T> {
T findById(int id);
//...more methods...
}
and its implementation class:
public class GenericDao<T> implements DaoInterface<T> {
#SuppressWarnings("unchecked")
private final Class<T> persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
#Autowired
protected SessionFactory sessionFactory;
#Override
#SuppressWarnings("unchecked")
public T findById(int id) {
Session session = sessionFactory.getCurrentSession();
return (T) session.get(persistentClass, id);
}
//...more methods...
}
Then my every concrete implementation class extends GenericDao and implements its interface.
I also have Service layer in my application. Some Services' methods completely delegate their work to DAO classes. So in the each concrete Service implementation I autowire appropriate DAO class and call its methods.
So now it looks:
public interface CustomerService {
Customer findById(int id);
}
and implementation:
#Service
#Transactional(readOnly = true, rollbackFor = Exception.class)
public class CustomerServiceImpl implements CustomerService {
#Autowired
private CustomerDao customerDao;
#Override
public Customer findById(int id) {
return customerDao.findById(id);
}
}
My question is - how to generify Service classes in the same way as DAO? So that my concrete class will look:
public class CustomerServiceImpl extends GenericService<Customer> implements CustomerService {
.....
}
The problem is that I cannot autowire DAO class in Generic Service:
#Autowired
private GenericDao<T> dao;
so that I could call dao's methods.
Should I do it in the constructor?
And one more question - where is the right place to annotate methods with #Transactional - in generic class or in every implementation class?
You have to create an instance of a generic Dao and put in the service layer some decision:
#Repository
public class GenericDao implements DaoInterface<T> {
//The type must be aquired at runtime,otherwise it may not be thread safe
#Autowired
protected SessionFactory sessionFactory;
#Override
#SuppressWarnings("unchecked")
public T findById(int id,Class<?> persistenceClass) {
Session session = sessionFactory.getCurrentSession();
return (T) session.get(persistenceClass, id);
}
//...more methods...
}
Also if you need a good generic repository layer take a look for Spring Data Jpa
This will make one and only one instance of the GenericDao.
Next you have 2 choice:
Create a singleton services for all your needs
Create a class service for every entity
abstract class GenericService<T> {
#Autowired
protected GenericDao dao;
#SuppressWarnings("unchecked")
protected final Class<T> persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
#Override
public T findById(int id) {
return dao.findById(id,persistenceClass);
}
}
Now every one of your service must extends the GenericService with a supplied persistence type and the job is done.
I am using Spring Data in my project and I have plenty of repositories. Now I wanted to add a method to some of the repositories, but not all of them, so I have created an interface LoggingRepositoryCustom, that (simplified) looks like this:
#NoRepositoryBean
public interface LoggingRepositoryCustom<T extends IEntity, ID extends Serializable> {
<S extends T> S save(S entity, AppUser author);
}
As I need to have a custom implementation of this, I have created also LoggingRepositoryImpl, that implements this interface:
#NoRepositoryBean
public class LoggingRepositoryImpl<T extends IEntity, ID extends Serializable> implements LoggingRepository {
#Override
public <S extends T> S save(S entity, AppUser author) {
//impl
}
}
Lastly, I have some repositories, that should have the functionity above, e.g. AppUserRepo:
#Repository
public interface AppUserRepo extends PagingAndSortingRepository<AppUser, Long>, LoggingRepositoryCustom<AppUser, Long> {
//methods of this repo
}
However, when I try to deploy this application, I get the following exception:
org.springframework.data.mapping.PropertyReferenceException: No property save found for type AppUser!
It seems that the custom implementation is not reflected and Spring Data tries to create a magical method from the name convention, thus looking for property "save" of AppUser, which does not exist. Is there a way to implement an interface, that is further extended by other interfaces?
I add the same issue in one of my project ... and i did as follow to get it working :
1 - create your "parent" interfaces and implementations :
Repository :
#NoRepositoryBean
public interface LoggingRepository<T extends IEntity, ID extends Serializable> extends PagingAndSortingRepository<T, Long>, LoggingRepositoryCustom<T, ID> {
}
Repository custom
#Transactional(readOnly = true)
public interface LoggingRepositoryCustom<T extends IEntity, ID extends Serializable> {
<S extends T> S save(S entity, AppUser author);
}
Implementation of the repository custom :
public class LoggingRepositoryImpl<T extends IEntity, ID extends Serializable> implements LoggingRepositoryCustom<T, ID> {
#Override
public <S extends T> S save(S entity, AppUser author) {
//impl
}
}
2 - Create your specific interfaces and implementations :
repository :
#Repository
public interface AppUserRepo extends LoggingRepository<AppUser, Long>, AppUserRepoCustom {
}
repository custom :
public interface AppUserRepoCustom<AppUser, Long> {
}
repository implementation :
public class AppUserRepoImpl extends LoggingRepositoryImpl<AppUser, Long> implements AppUserRepoCustom {
}
hope this helps
I have developed a GenericDAO interface with two generic types, the entity and the type of the primary key:
public interface GenericDAO<E, PK extends Serializable> {
PK save(E newInstance);
void update(E transientObject);
//typical dao methods
}
Then I have provided an implementation for them in hibernate 4:
#Transactional
#Component
#Repository
public abstract class GenericDAOHibernate4<E, PK extends Serializable> implements GenericDAO<E, PK> {
public PK save(E newInstance) {
return (PK) factory.getCurrentSession().save(newInstance);
}
public E findById(PK id) {
return (E) factory.getCurrentSession().get(getEntityClass(), id);
}
//method implementations
}
Then I just have to create concrete classes extending this abstract class:
#Component
#Transactional
#Repository
#Qualifier("userDAO")
public class UserDAO extends GenericDAOHibernate4<User, Long> {
#Autowired
public UserDAO(SessionFactory factory) {
super(factory);
}
#Override
protected Class<User> getEntityClass() {
return User.class;
}
}
Then I inject the concrete DAOs when needed this way:
public class UserService extends GenericService<User> {
#Autowired
public UserService(#Qualifier("userDAO") GenericDAO<User, Long> dao) {
super(dao);
}
But, if I need to add another method to the concrete dao, and therefore inject the concrete class, spring cannot find the dependency. This fails at startup:
public class UserService extends GenericService<User> {
#Autowired
public UserService(#Qualifier("userDAO") UserDAO dao) {
super(dao);
}
With this error:
Could not instantiate bean class [ddol.rtdb.services.UserService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: ddol.rtdb.services.UserService.()
How should I inject it?
If a class implements an interface, a bean of that class can only be autowired using the interface type and not the concrete class type. Since UserDao implements the GenericDAO<User, Long> interface, it is being autowired correctly when you autowire using the interface. Spring is not able to find the dependency when you try to autowire using the concrete class, then it looks for a no-arg constructor for UserService and fails giving the particular error on not finding any.
In general, it not a good idea to inject dependencies using concrete classes because it tightly couples your modules. Right way to do this is to have an interface for each of your DAO classes and have their implementation extending the GenericDAOHibernate4.
public interface GenericDAO<E, PK extends Serializable> {
PK save(E newInstance);
void update(E transientObject);
//typical dao methods
}
public interface UserDAO extends GenericDAO<User, Long> {
List<User> findUsersByFirstname(String firstName);
}
#Component
#Transactional
#Repository
#Qualifier("userDAO")
public class UserDAOImpl extends GenericDAOHibernate4<User, Long>
implements UserDAO {
#Autowired
public UserDAO(SessionFactory factory) {
super(factory);
}
#Override
protected Class<User> getEntityClass() {
return User.class;
}
List<User> findUsersByFirstname(String firstName) {
//provide implementation here
}
}
public class UserService extends GenericService<User> {
#Autowired
public UserService(#Qualifier("userDAO") UserDAO dao) {
super(dao);
}
}
#Autowired = wire by Type
#Resource = wire by bean name