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> {
}
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 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?
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
}
I'm implementing GenericDao. I have problem with 2 methods - getAll() and getById(Long id), entity class has null value. It looks like the class is not setted. How Can I solve this problem ?
#Repository
public class GenericDaoImpl<T> implements GenericDao<T> {
private Class<T> clazz;
#Autowired
SessionFactory sessionFactory;
public void setClazz(final Class<T> clazzToSet) {
this.clazz = clazzToSet;
}
public T getById(final Long id) {
return (T) this.getCurrentSession().get(this.clazz, id);
}
public List<T> getAll() {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
this.clazz);
return criteria.list();
}
protected final Session getCurrentSession() {
return this.sessionFactory.getCurrentSession();
}
}
PersonDao
public interface PersonDao extends GenericDao<Person> { }
PersonDaoImpl
#Repository("PersonDAO")
public class PersonDaoImpl extends GenericDaoImpl<Person> implements PersonDao {}
Service:
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Transactional
public List<Person> getAll() {
return personDao.getAll();
}
#Transactional
public Person getById(Long id) {
return personDao.getById(id);
}
}
You must set the clazz property of PersonDao. This can be done by declaring a post initialization callback with the #PostConstruct annotation.
#Repository("PersonDAO")
public class PersonDaoImpl extends GenericDaoImpl<Person> implements PersonDao {
#PostConstruct
public void init(){
super.setClazz(Person.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;
}
}