Spring Data JPA Custom Methods & Component Injection - java

I configured a Spring Data JPA Custom Method extending my app repository functionalities. I referenced this tutorial link and It works properly. I implemented the custom method because I have to set the user in all the JPA Entities before persisting.
This is the BaseRepository interface.
#NoRepositoryBean
public interface BaseRepository<T extends AuditableBean, ID extends Serializable> extends CrudRepository<T, ID> {
<S extends T> S save(S bean);
}
This is the BaseRepository Implementation
public class BaseRepositoryImpl <T extends AuditableBean, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {
#Inject private CloudContextProvider cloudContextProvider; // <-- I inject this component here but it is null.
private final EntityManager entityManager;
public BaseRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
public T populateUser(T t) {
t.setUpdaterUser(cloudContextProvider.getUserId());// <-- Here throws a NPE
return t;
}
#Transactional
#Override
public <S extends T> S save(S bean) {
this.auditUser(bean);// <-- This is the custom method.
return super.save(bean);
}
public T auditUser(T t) {
t = populateUser(t);
return t;
}
}
So the question is, ¿How can I inject the cloudContextProvider component in the BaseRepositoryImpl?.
Is that possible?
Maybe with constructor injection?

Related

Is it possible to register with Container without using #Repository or #Bean?

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> {

How to add custom method all services spring data

I customizei a repository for all my repositories children have the same methods. The code is below the repository implementation.
This interface:
#NoRepositoryBean
public interface BaseMyRepository<T, ID extends Serializable> extends JpaRepository<T, ID>{
List<T> findCustomNativeQuery(String sqlQuery);
}
This implementation class:
public class BaseMyRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseMyRepository<T, ID>{
private final EntityManager entityManager;
public BaseMyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager){
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
#Transactional
#Override
public List<T> findCustomNativeQuery(String sqlQuery) {
List<T> lista = entityManager.createNativeQuery(sqlQuery, this.getDomainClass()).getResultList();
return lista;
}
}
This my repository:
public interface MyRepository extends BaseMyRepository<SmaempreEntity, Integer>{
}
Now I need to know if is possible do exemplify the code below:
#Service
#Transactional
public class MyBaseService<R extends BaseMyRepository, E> {
#Autowired
private R;
public List<E> findAll() {
return R.findAll();
}
public List<E> findCustomNativeQuery(String sqlQuery) {
return R.findCustomNativeQuery(sqlQuery);
}
}
public class MyService extends MyBaseService<MyRepository, MyEntity> {
}

Injecting bean inside a custom all repository

Am implementing a custom all Spring Data Jpa repository like
#NoRepositoryBean
public interface TenantAwareRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
#Override
Page<T> findAll(Pageable pageable);
}
and implemented it like
#Slf4j
public class TenantAwareRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements UserAwareRepository<T, ID>{
#Autowired
private ResourceServerTokenServices defaultTokenServices;
private final EntityManager entityManager;
public TenantAwareRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
// Keep the EntityManager around to used from the newly introduced methods.
this.entityManager = entityManager;
}
public Page<T> findAll(Pageable pageable){
OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
log.info("Token = {}", details.getTokenValue());
return null;
}
}
Unfortunately defaultTokenServices does not get injected and is null. How do I inject a spring bean in a custom-all repository implementation.
I am trying to add custom behavior to all repositories:
Ths issue is that TenantAwareRepositoryImpl class should implement TenantAwareRepository like shown below:
#Component
#Slf4j
public class TenantAwareRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements TenantAwareRepository<T, ID>{
//add your code here
}

Dependency not found when trying to inject a generic class

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

Overriding the persistentce context unitName declared for the EntityManager in a parent class.

I have a generic DAO class that looks like this:
public class GenericDaoJpa <T extends DomainObject> implements GenericDao<T> {
private final Class<T> type;
#PersistenceContext(type=PersistenceContextType.TRANSACTION, unitName="myPersistenceUnit")
protected EntityManager entityManager;
public GenericDaoJpa(Class<T> type) {
super();
this.type = type;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public T get(Object id) {
return (T) entityManager.find(type, id);
}
}
The implementation DAO class looks like this:
#Repository("appDao")
public class ProductDaoJpa extends GenericDaoJpa<Product> implements ProductDao{
public ProductDaoJpa() {
super(Product.class);
}
public List<Product> getAllProducts() {
return getAll();
}
}
I have configured another persistentUnit called mySecondPersistenceUnit for a different database. I would like to create a new DAO class that will also extend the GenericDaoJpa class but use a different persistent unit. How can i extend the GenericDaoJpa class but use a different persisitent unit for each DAO?
I tried moving this declaration to each of the DAO classes but this causes the parent class not to compile because it has no reference to the entityManager.
#PersistenceContext(type=PersistenceContextType.TRANSACTION, unitName="myPersistenceUnit")
protected EntityManager entityManager;
Try to use method injection instead:
public class GenericDaoJpa <T extends DomainObject> implements GenericDao<T> {
#PersistenceContext(type=PersistenceContextType.TRANSACTION, unitName="myPersistenceUnit")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}
While child class that need use different PU:
#Repository("appDao")
public class ProductDaoJpa extends GenericDaoJpa<Product> implements ProductDao{
#Override
#PersistenceContext(type=PersistenceContextType.TRANSACTION, unitName="mySecondPersistenceUnit")
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}

Categories