How to refresh a HashMap in Java which is already loaded during startup using #PostConstruct annotation - java-8

I'm working on a Java application(micro-services) using Spring 5, JDK 1.8, SpringBoot 2.0. I got a helper class where I'm loading a hashmap using the #PostConstruct like below :-
Helper class:-
private final Map<String, CommonData> empMap = new HashMap<>();
#PostConstruct
public void init() {
loadEmpMap();
}
private void loadEmpMap() {
List<EmpMap> employees = empRepo.getEmpInfo();
employees.forEach(p -> empMap
.put(p.getEmpId(),
new CommonData(p.getName(), p.getDesignation(), p.getContactNumber())));
}
Now during the application startup, #PostConstruct will be called and HashMap will be loaded with data using JPA Repository. This HashMap will be available through out this object to use. Now my requirement is to update (auto-refresh) this HashMap with new set of data (ofcourse entity refresh) whenever there is an update/save operation on entity. For this, I have written an Interface like below to Refresh the entity using the EntityManager :-
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
#NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
void refresh(T t);
}
public class CustomRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> {
private final EntityManager entityManager;
#Autowired
public CustomRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
#Override
#Transactional
public void refresh(T t) {
entityManager.refresh(t);
}
}
And then extending this custom repository to my application respository like below :-
#Repository
public interface EmpRepo extends CustomRepository<Employee, EmpKey> {
}
public final class CommonRepositoryDetails implements EmpRepo {
private EmpRepo empRepo;
constructor(){
}
XYZMethod(){
-------
---- some line of code for save/update operation using Jpa -----
-- then trying to refresh the entity as below -----
empRepo.refresh(value);
}
I'm not sure whether this will refresh my hashmap again with latest objects from entity to be used or Im missing something here. Please let me know as I want to refresh my HashMap again. Thanks

Since the HashMap belongs to the HelperClass it will be not updated.
I am assuming that you are going to use employee information for the HashMap in the application, because you don't want to request the database everytime to fetch the records. You can use Spring Cahche mechanism to save the information in the SpringDefault Cache(Concurrent HashMap).
You can use #Cacheable on get methods, it will hit the database when the information is not present in the cache. #CachePut annotation the update method will update the cache.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html
http://www.baeldung.com/spring-cache-tutorial
https://spring.io/guides/gs/caching/

Related

How to access Entity Manager within a service in spring boot project?

I've googled so many times for how to access entity manager in spring boot, and did what posts said, but it didn't work. I want to access Entity Manager so that i can do some custom query operation. Here i defined the custom interface in a dependent package named 'customrepository':
public interface PostRepositoryCustom {
void refresh(Post post);
}
Then I implemented this interface in another package named 'customrepositoryimpl':
public class CustomPostRepositoryImpl implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public void refresh(Post post) {
em.refresh(post);
}
}
Finally, I defined a standard repository interface which extends 'CrudRepository' and the custom repository:
public interface PostRepository extends CrudRepository<Post,Long>,PostRepositoryCustom {}
Every steps i followed What i googled and Official Documents, BUT when i run my application, i get this:
Error creating bean with name 'postRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract void com.example.demo.customrepository.PostRepositoryCustom.refresh(com.example.demo.model.Post)! No property refresh found for type Post!
Why? Anyone tell me where should i correct my mistakes?
Spring cannot locate the beans, so try annotate with #Repository
#Repository
public class CustomPostRepositoryImpl implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public void refresh(Post post) {
em.refresh(post);
}
}
and
#Repository
public interface PostRepository extends CrudRepository<Post,Long>{
}

Spring Boot JPA #Transactional #Service does not update, but #Transactional in controller does

I have a very basic Spring Boot/JPA stack app, with a controller, service layer, and repository that does not persist updates as I understand it should.
A trivial Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
protected Customer() {}
public Customer(String name) { this.name = name; }
// standard getters,setters //
}
A trivial Repository:
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {}
A simple Service layer:
// If the service is #Transactional and the controller is not, the update does NOT occur
#Transactional
#Service
public class CustomerService {
private static final Logger LOG = getLogger(CustomerService.class);
#Autowired
private CustomerRepository customerRepository;
boolean updateCustomerName(Long id, String name) {
Customer customer = customerRepository.findOne(id);
if (customer == null) { return false; }
// Modifies the entity
customer.setName(name);
// No explicit save()
return true;
}
}
And a REST controller that uses it all:
// If the controller is #Transactional and the service is not, the update occurs
#RestController
#RequestMapping("/mvc")
public class CustomerController {
#Autowired
private CustomerService customerService;
#RequestMapping(path = "{id}", method = RequestMethod.PUT)
public ResponseEntity updateCustomerName(#PathVariable Long id, #RequestParam("name") String name) {
customerService.updateCustomerName(id,name);
return ResponseEntity.noContent().build();
}
}
These are wired together with a simple one-liner SpringBootApplication
I have SQL debug logs enabled and see the selects, update, etc.
With the code above: When the service method is invoked by the controller, the modified entity is not persisted. SQL logs show the select of the entity but no update.
There is also no update if nothing is marked #Transactional
However, simply by moving the #Transactional annotation from the service class to the controller class, the SQL update does occur.
If I add an explicit customerRepository.save(customer) to the service method, the update also occurs. But my understanding is that the ORM should automatically save modified persistent entities.
I'm sure the issue has something to do with the EntityManager lifecycle in the web request, but I'm puzzled. Do I need to do additional configuration?
Complete example at https://github.com/monztech/SO-41515160
EDIT: This was solved, see below. Per the Spring spec #Transactional does not work in package-private methods and mistakenly did not make the update service method public.
The update will occur if the method is public and the service class has the #Transactional annotation.
I do have another question, however. Why is the #Transactional annotation necessary? (the update does not occur without it) Shouldn't the entity manager still persist the object because of the open session in view mechanism that Spring uses, independent of any transaction?
Make your updateCustomerName method public.

Spring Data JPA custom method causing PropertyReferenceException

I have been trying to create a spring boot application. In my application I would like to add some custom methods to save the data instead of using the default save method.
My application entry point is something like this:
#Configuration
#ComponentScan
#EnableJpaRepositories(repositoryImplementationPostfix = "CustomImpl")
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
#PropertySource("application.properties")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have changed this line repositoryImplementationPostfix to even Impl but, it didn't work.
My CrudRepository
#RepositoryRestResource
public interface TaRepository extends CrudRepository<Ta, Integer> ,TestRepository{
List<Ta> findByName(#Param("name") String name);
}
My Custom Repository:
public interface TestRepository {
public void myCustomMethod(TestDto dto);
}
My Custom Repository Impl
public class TestRepositoryCustomImpl implements TestRepository{
#PersistenceContext
private EntityManager em;
#Override
public void myCustomMethod(TestDto model){
}
NOTE:
If I change my CrudRepostory from the mentioned to this:
#RepositoryRestResource
public interface TaRepository extends CrudRepository<Ta, Integer> {
List<Ta> findByName(#Param("name") String name);
}
everything works fine. But not with the custom method implementation.
For Spring Data JPA #Repository or #RepositoryRestResource you never need to implement a Custom Interface. For any simple query you can create any kind of method, please follow the simple guide.
http://docs.spring.io/spring-data/jpa/docs/1.4.1.RELEASE/reference/html/jpa.repositories.html
For a complex query you can use JpaSpecificationExecutor.
How can I create a Predicate from a HQL query?

How to access entity manager with spring boot and spring data

How can I get access to the Entity Manager in the repository when using Spring Boot and Spring Data?
Otherwise, I will need to put my big query in an annotation. I would prefer to have something more clear than a long text.
You would define a CustomRepository to handle such scenarios. Consider you have CustomerRepository which extends the default spring data JPA interface JPARepository<Customer,Long>
Create a new interface CustomCustomerRepository with a custom method signature.
public interface CustomCustomerRepository {
public void customMethod();
}
Extend CustomerRepository interface using CustomCustomerRepository
public interface CustomerRepository extends JpaRepository<Customer, Long>, CustomCustomerRepository{
}
Create an implementation class named CustomerRepositoryImpl which implements CustomerRepository. Here you can inject the EntityManager using the #PersistentContext. Naming conventions matter here.
public class CustomCustomerRepositoryImpl implements CustomCustomerRepository {
#PersistenceContext
private EntityManager em;
#Override
public void customMethod() {
}
}
In case you have many repositories to deal with, and your need in EntityManager is not specific for any particular repository, it is possible to implement various EntityManager functionality in a single helper class, maybe something like that:
#Service
public class RepositoryHelper {
#PersistenceContext
private EntityManager em;
#Transactional
public <E, R> R refreshAndUse(
E entity,
Function<E, R> usageFunction) {
em.refresh(entity);
return usageFunction.apply(entity);
}
}
The refreshAndUse method here is a sample method to consume a detached entity instance, perform a refresh for it and return a result of a custom function to be applied to the refreshed entity in a declarative transaction context. And you can add other methods too, including query ones...

Spring Hibernate : Generic Dao addition causes - org.hibernate.TransactionException: nested transactions not supported

I have a layered architecture in my project.
in order to prevent redundancy i created a very basic generic dao:
public interface GenericDAO {
public <T> T getItemById(long id, Class<T> c);
public <T> int save(T... objectsToSave);
public <T> int saveOrUpdate(T... objectsToSave);
public <T> int delete(T... objectsToDelete);
.
.
}
now all my other dao's uses this generic dao as a private field in order to use its basic methods:
i.e:
#Repository
public class UserDAOHibernateImpl implements UserDao {
#Autowired
private GenericDAO dao;
#Override
public int deleteUser(User u) {
return dao.delete(u);
}
.
.
.
}
My services are like this :
#Service
public class UserServiceHibernateImpl implements UserService {
#Autowired
private UserDao userDao;
#Transactional(readOnly = false)
public int deleteUser(User u) {
return userDao.deleteUser(u);
}
.
.
.
}
Problem is that:
ApplicationContext ctx = new ClassPathXmlApplicationContext("root-context.xml");
UserServiceHibernateImpl d = ctx.getBean("userServiceHibernateImpl", UserServiceHibernateImpl.class);
User u = d.getUserById(1);
throws the exception:
Exception in thread "main" org.hibernate.TransactionException: nested transactions not supported
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:152)
at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1426)
at src.com.plantaware.model.dao.impl.hibernate.GenericDAOHibernateImpl.getItemById(GenericDAOHibernateImpl.java:80)
at src.com.plantaware.model.dao.impl.hibernate.GenericDAOHibernateImpl$$FastClassByCGLIB$$be31a192.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
Removing the
#Autowired
private GenericDAO dao;
from my service will solve this, but that mean ill have to use redundant code on each one of my services, that's missing the point completely of using a generic dao.
any ideas why?
BTW: I'm using CGLIB proxy since without that I'm getting the "Bean named 'X' must be of type Y but was actually of type [$Proxy]" exception
thanks..
You are mixing Spring-specific declarative transaction management (#Transactional) with Hibernate-specific manual transaction management (beginTransaction(), etc).
If you use #Transactional you don't need to call beginTransaction(), etc in your DAO, because necessary transaction management is already provided by Spring. Remove manual transaction management code from your DAO.
See also:
11. Transaction Management
TransactionException: nested transactions not supported
means that whenever you start a transaction, you cannot start another inside it. You first need to finish it. Here, don't autowire dao inside dao
#Repository
public class UserDAOHibernateImpl implements UserDao {
#Autowired
private GenericDAO dao; // this is not acceptable.
#Override
public int deleteUser(User u) {
return dao.delete(u);
}
.
.
.
}
All business logic must be inside service layer. Dao aims only db access. Put all necessary dao beans inside your services.

Resources