How to add a user define annotation in spring JPA save method - java

How to add a user define annotation in spring jpa save method only.
I have created a annotation and wanted to use it on the save method of the repository, but the save method is inherited method from CrudRepository of JPA, not sure how can annotation be applied on only that method and not the other method of that repository.
Tried overriding that method in the repository interface and applied the annotation but it didn't worked
Please refer the code below -
Annotation :
#Target({ ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
}
#Aspect
#Configuration
#Slf4j
#ComponentScan(value = "com.somepackage.service")
public class MyAnnotationInterceptor {
#Value("${val}")
private String val;
#Around("#annotation(com.somepackage.service.application.annotation.MyAnnotation)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
if("TEST".equalsIgnoreCase(val)){
log.info("Test Event")
}else{
joinPoint.proceed();
}
}
}
Repository :
#Transactional
public interface EmployeeEntityRepository extends CrudRepository<EmployeeEntity, String> {
List<EmployeeEntity> findAllByEmpIdAndStatusNot(String empId, String status);
#Query("SELECT emp.empId FROM EmployeeEntity emp WHERE emp.orderId IN ?1")
List<String> findEmployeeIds(List<String> orderIds);
#Override
#MyAnnotation
<S extends EmployeeEntity> Iterable<S> save(Iterable<S> iterable);
}
Service Class:
class EmployeeService {
#Autowired
EmployeeEntityRepository employeeEntityRepo;
public void saveEmployee(List<EmployeeEntity> employeeData) {
employeeEntityRepo.save(employeeData);
employeeEntityRepo.clearCache(employeeData);
/***
.
.
.
Some other logic calculations
.
.
***/
}
}

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 execute a query using #Query Spring JPA annotation

I am trying to write a query using SpringData Jpa using the #Query annotation on the interface method declaration.
The interface looks like this:
public interface MyService {
#Query("select * from employee e where e.projectId = ?1")
public List<Employee> getEmployeesWorkingOnAProject(String projectId) throws MyException;
}
I also have a class that implements this interface:
#Component
public class ProjectServiceImpl implements ProjectService {
}
I am not sure how will this query execution work and how to provide an implementation for getEmployeesWorkingOnAProject method in the imeplementing class.
Thanks
In your Interface, you should extend JpaRepository (or any other spring data repository).
Then you can just autowire your interface in any spring bean class and call getEmployeesWorkingOnAProject().
So for example:
public interface MyService extends JpaRepository<Employee,Long> {
#Query("select * from employee e where e.projectId = ?1")
public List<Employee> getEmployeesWorkingOnAProject(String projectId) throws MyException;
}
#Component
public class ProjectServiceImpl implements ProjectService {
private final MyService service;
#Autowire // not necessary in spring 4.3 +
public ProjectServiceImpl(MyService service) {
this.service = service;
}
public List<Employee> getEmployeesWorkingOnAProject(String projectId) throws MyException {
return service.getEmployeesWorkingOnAProject();
}
}
However, Spring Data is able to build a query for you, so is no reason for writing your own query in this example.
Spring Data way:
public interface MyService extends JpaRepository<Employee,Long> {
public List<Employee> findAllByProjectId(String projectId) throws MyException;
}
First things first. Your interface have to extend some kind of Spring Data Repository, for example JpaRepository.
Second thing, in the Query annotation, you can put two types of query. JPQL or native SQL query. This can be controlled by a flag on the query annotation (nativeQuery).
In JPQL, your query should look like the following:
#Query("select e from employee e where e.projectId = ?1")

Not injected bean in class that extends abstract class in Spring Boot

I have trouble with initializing bean and injecting JPA repository into one particular bean. No idea why it doesn't work...
There is a interface defining key service:
public interface KeyService {
Store getKeyStore();
Store getTrustStore();
}
and abstract class that implements this interface:
public abstract class DefaultKeyService implements KeyService {
abstract KeyRecord loadKeyStore();
abstract KeyRecord loadTrustStore();
/* rest omitted... */
}
and base class that extends abstract class:
#Service
public class DatabaseKeyService extends DefaultKeyService {
#Autowired
private KeyRecordRepository keyRecordRepository;
#Override
protected KeyRecord loadKeyStore() {
return extract(keyRecordRepository.findKeyStore());
}
#Override
protected KeyRecord loadTrustStore() {
return extract(keyRecordRepository.findTrustStore());
}
/* rest omitted... */
}
And bean initialization:
#Bean
public KeyService keyService() {
return new DatabaseKeyService();
}
This is a KeyRecordRepository repository:
public interface KeyRecordRepository extends Repository<KeyRecord, Long> {
KeyRecord save(KeyRecord keyRecord);
#Query("SELECT t FROM KeyRecord t WHERE key_type = 'KEY_STORE' AND is_active = TRUE")
Iterable<KeyRecord> findKeyStore();
#Query("SELECT t FROM KeyRecord t WHERE key_type = 'TRUST_STORE' AND is_active = TRUE")
Iterable<KeyRecord> findTrustStore();
KeyRecord findById(long id);
}
Question: is there some reason why keyRecordRepository in DatabaseKeyService class is still null? Really I have no idea why only this this field is not injected. Other beans and repositories works perfectly fine.
Couldn't be a problem because parent class is an abstract class?
DatabaseKeyService must be annotated with #Component to be a Spring managed bean.
Your problem is related with having 2 beans for class DatabaseKeyService. One from configuration class - #Bean annotation and second from #Service annotation.
Probably when you remove
#Bean
public KeyService keyService() {
return new DatabaseKeyService();
}
injecting with #Service will be work.
If you want use #Bean you must add KeyRecordRepository. I prefer using constructor injection so firstly create it in DatabaseKeyService
public DatabaseKeyService(KeyRecordRepository keyRecordRepository) {
this.keyRecordRepository = keyRecordRepository;
}
Then in your configuration file
//other
#Autowired
private KeyRecordRepository keyRecordRepository;
#Bean
public KeyService keyService() {
return new DatabaseKeyService(keyRecordRepository);
}

Generifying Service layer classes

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.

Method in interface implementing class not visible in service class

I have a method defined in an implementing #Repository class that is an extension of an abstract base class and implements an interface. However, a method defined in the #Repository class is not visible in the #Service class, and I am not sure why.
I have a #Service class defined as
#Service
#Transactional
public class CategoryService {
#Autowired
private IJpaRepository categoryRepository;
public CategoryService(){ }
/* service methods */
public List<Category> findTopLevelCategories(){
//findTopLevelCategories is not visible here
return categoryRepository.findTopLevelCategories();
}
}
where IJpaRepository is an interface defined by
public interface IJpaRepository<T> {
T findOne(int id);
List<T> findAll();
T create(T entity);
T update(T entity);
void delete(T entity);
void deleteById(int id);
}
and the #Repository is defined as an extension of an abstract class implementing the interface above:
#Repository
public class CategoryRepository extends AbstractJpaRepository<Category> implements IJpaRepository<Category> {
public List<Category> findTopLevelCategories(){
Query queryCategoryTopLevel = getEntityManager().createNamedQuery("findTopLevelCategories");
return queryCategoryTopLevel.getResultList();
}
/* Other overriding methods */
}
My best guess is that it is due to the #Autowired binding to the IJpaRepository, rather than the actual implementing CategoryRepository class. Without making any changes to IJpaRepository interface, how can I make the new method defined in CategoryRepository visible to the #Service class?

Categories