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

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.

Related

How to mock context.getBeansWithAnnotations with Mockito

I have created an interface Client with its two concrete implementations
clientA and clientB and annotated them with my custom annotation.
public interface Client{
public void dosomething();
}
#Component
#Myannotation
public class clientA implements Client {
public void doSomething(){
sysout("Client A do something");
}
}
#Component
#Myannotation
public class clientB implements Client {
public void doSomething(){
sysout("Client B do something");
}
}
Now I am calling the overriden methods of both clientA and clientB from Alien class.
#Component
class Alien{
#Autowired
private ApplicationContext context;
public void performOperation(){
Map<String, Object> beans =
context.getBeansWithAnnotation(MyAnnotation.class);
for(Map.Entry<String, Object> entry: beans.entrySet()) {
Client c = (Client)entry.getValue();
c.doSomething();
}
}
}
I am facing problem with writing test method for performOperation.
#RunWith(MockitoJUnitRunner.class)
class AlienTest
{
#InjectMocks
Alien a;
#Test
public void testperformOperation(){
//how to Mock for beans
assertEquals(expected, a.performOperation());
}
}
1) How should I write testperformOperation method(allowed to change the return type of performOperation method from void to any other type)
2) Is there any better way to get list of all implementations for Client interface without creating custom annotations.
I would suggest you first refactoring Alien to make it more testable using Dependency Injection idea which its dependencies (i.e Client) can be injected from outside rather than hard coded inside a method which always get from the spring context:
#Component
public class Alien{
private List<Client> clients = new ArrayList<>();
#Autowired
public Alien(List<Client> clients) {
this.clients = clients;
}
public void performOperation(){
for(Client c: clients) {
c.doSomething();
}
}
}
If you simply want to inject all Client implementation to the Alien , you just need to #Autowired List<Client> into Alien which Spring will already help you to inject all the Client implementation to it out of the box. No need to create #Myannotation
Once you make the Alien 's dependencies injectable (i.e a list of client) , you can simply inject a mock to it and verify performOperation() really invoke all of Client 's doSomething():
#RunWith(MockitoJUnitRunner.class)
class AlienTest{
#Mock
private Client mockClientA;
#Mock
private Client mockClientB;
#Test
public void testperformOperation(){
List<Client> clients = new ArrayList<>();
clients.add(mockClientA);
clients.add(mockClientB);
Alien alien = new Alien(clients);
alien.performOperation();
verify(mockClientA).doSomething();
verify(mockClientB).doSomething();
}
}
I’ll answer both parts of your question, but I believe the first approach is inferior and the second is the go-to approach.
If you want to stick with your custom annotation approach, you need to have a #Mock ApplicationContext applicationContext in your test class. In the test method (or setup method) you need to mock the call to applicationContext.getBeansWithAnnotation and return an appropriate map containing your bean (possibly also a mock)
You can easily inject all beans to a class by injecting a List of the appropriate type. In your case
get rid of #Autowired ApplicationContext
add an #Autowired List (or, preferably, use constructor injection)
This will also make the tests simpler, no need to mock ApplicationContext.
For example, see https://dzone.com/articles/load-all-implementors

Transactional and Stream in Spring

I try to understand why this code doesn't work
In component:
#PostConstruct
public void runAtStart(){
testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
And repository :
public interface PersonRepository extends JpaRepository<Person, Long> {
Stream<Person> findTop10ByFirstName(String firstName);
}
I get:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses #Transactional or any other way of declaring a (read-only) transaction.
One key thing about Spring is that many annotated features use proxies to provide the annotation functionality. That is #Transactional, #Cacheable and #Async all rely on Spring detecting those annotations and wrapping those beans in a proxy bean.
That being the case, a proxied method can only be used when invoked on the class and not from within the class. See this about the topic.
Try:
Refactoring and call this #Transactional method from another class in your context, or
By self-autowiring the class into itself and calling the #Transactional method that way.
To demonstrate (1):
public class MyOtherClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional`
myTestStreamClass.testStream();
}
}
To demonstrate (2):
#Component
public class MyTestStreamClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional` since it's self-autowired
myTestStreamClass.testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
}

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

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/

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

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

Resources