How to create non-transactional JUnit integration tests in Spring? - java

An integration test class is annotated with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = IntegrationTestConfig.class)
It's not supposed to run in a transaction so isn't marked as #Transactional but I'm getting errors when trying to perform persist, merge etc. operations on the EntityManager, which is injected using #PersistenceContext:
No transactional EntityManager available
How can this be resolved?
EDIT:
As requested in the comments, the Spring version is 4.1.0.RELEASE and IntegrationTestConfig is below:
#EnableAspectJAutoProxy
#EnableAsync
#EnableScheduling
#EnableTransactionManagement
#Configuration
public class IntegrationTestConfig {
/**
* Override the existing JPA data source bean with a test data source.
* #return test data source
*/
#Bean
public DataSource dataSource() {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(org.h2.Driver.class);
dataSource.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS mydb");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
}

If you are sure that you are never going to call entityManager.flush(), obtain the PersistenceContext as follows:
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
Why is this needed? Spring Data JPA hands out what is called a shared EntityManager when the #PersistenceContext annotation is used (without any attributes). Full details for this are available in the JavaDocs for org.springframework.orm.jpa.SharedEntityManagerCreator. This class maintains a lookup table where the EntityManager methods flush, merge, persist, refresh and remove are required to be run inside a transaction. So, any time it encounters a method call that is not inside a transaction, it bails out.
The annotation #PersistenceContext has a type attribute that can be set to one of PersistenceContextType.EXTENDED or PersistenceContextType.TRANSACTION, with the later being the default. Therefore, the default #PersistenceContext causes SharedEntityManagerCreator to look for a transaction and bail out if none is found.
Using PersistenceContextType.EXTENDED bypasses the need to check for a transaction when obtaining the EntityManager and therefore the code should work.
flush still cannot be called without a transaction because the JPA providers require it to be called only within a transactional context.

Related

Spring boot, Transactional annotation not working on Service, SimpleJPA repository Transactional partially deleting

Calling JPArepository to deleteAllById Debug Snap Internally using hibernate Provider for JPA
Implementation Class SimpleJpaRepository has Transactional Annotation at the top
Due to Transactional Annotation it went into Proxy Debug Snap
In loop it is deleting in bulk, Deleting one by one in debug mode, when 1 element is deleted checking in database, element still Exist, but then post 1 element deletion forcefully throwing exception, then 1 delete is being committed in database (Any configuration missing here)? see there is some option of Transactional(rollback = "Exception.class"), but how to add it in SimpleJPARepository class ?
Debug Snap
Main issue, I am putting Transactional annotation at top of service method called by rest API, which is internally calling multiple other services which are actual doing JPA transaction like update/delete/create (So transactional annotation on top of my service is not going in Proxy as per step 2 debug (But internal JPA calls going into Proxy), checked many things, public method...) still nothing worked
StartupClass Configuration Annotation
#SpringBootApplication
#ImportResource("classpath:spring/starter.xml")
#PropertySource("classpath:config/${env}/application.properties")
#EnableJpaAuditing()
#EnableTransactionManagement
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
Transaction Manager Main
#Bean()
#Primary
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}

Perform native SQL Query during Spring startup

I'm using Spring and Hibernate with an automatically generated database (for that I have set "hibernate.hbm2ddl.auto" to "update" in the JPA configuration properties).
I also have a class annotated #Configuration with a #PostConstruct method that is called on application startup after the database has been created or updated. This is where I setup the database with some default data if it's empty (first launch).
I would like to execute some custom native SQL queries at this moment. These queries won't return anything, they're just configuration stuff (like creating additional indexes or extensions).
Currently I'm stuck on creating a SessionFactory in order to create a new Hibernate Session. I've tried auto wiring it, but it doesn't work :
#Autowired
SessionFactory sessionFactory;
Gives me: Field sessionFactory in ... required a bean of type 'org.hibernate.SessionFactory' that could not be found.
I understand that I probably need to configure it elsewhere, but I don't know where. Several answers on SO use an xml configuration file, but I'm not using any configuration file so I can't do it that way.
Is there a way Spring can create the SessionFactory with the appropriate configuration ?
You don't even need to access SessionFactory. Please just put your scripts into a file src/main/resources/scripts/myscript.sql. You can then do the following with Spring:
#Component
public class Startup {
#Autowired
private DataSource dataSource;
#PostConstruct
public void runNativeSql() {
ClassPathResource resource = new ClassPathResource("scripts/myscript.sql");
try(Connection connection = dataSource.getConnection()) {
ScriptUtils.executeSqlScript(connection, resource);
} catch (SQLException | ScriptException e) {
//LOG
}
}
}
You can autowire the JPA EntityManager as:
#PersistenceContext
EntityManager entityManager;
If you really need a Hibernate Session and are using using JPA 2.1, the Session can be obtained from the EntityManager as:
entityManager.unwrap(Session.class);

Calling a Native Query in the same request life cycle as a JPA managed query causes automatic persist

I'm sure this is a feature, but I would appreciate any pointers/help on the issue.
I'm using Spring (Boot) / JPA (Hibernate) stack.
If I query for an entity (called Homes) using JPA, and increment a Calendar field
homeInstance.getListedDate().add(Calendar.DATE, 1), for example
with no intention of saving this change back to the database (it's only for quick intermediary calculations as the several routines run on a list of these entities).
Then I call a nativequery using an injected EntityManager bean.
#Autowired
EntityManager em;
...
Query nvqry = em.createNativeQuery(...)
nvqry.getResultList()
Doing this automatically persists the changes made to the entity above (which were never supposed to be persisted.
Is there a way to disable this feature without losing the in-memory changes? I manually persist anything I want using the repository, and as such a whole session persistence is useless for me.
That's related to the hibernate FlushMode. If you haven't modified the default configuration, this will be likely set to FlushMode.AUTO.
To alter this behavior you have to provide an EntityManagerFactory bean.
In the configuration pojo you can declare something like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final DataSource dataSource,
final JpaVendorAdapter jpaVendorAdapter) {
final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("domainPC");
factory.setDataSource(dataSource);
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
Properties jpaProperties = new Properties();
//and here is your flushMode set.
jpaProperties.setProperty("org.hibernate.flushMode", "COMMIT");
factory.setJpaProperties(jpaProperties);
return factory;
}

Spring transactions, EntityManager lifecycle

I am developing a project with spring framework.
I have following architecture: all database code is in dao classes, all application logic is in service classes, http requests are processed using spring mvc rest controllers.
#Repository
public class UserDao {
#PersistenceContext
private EntityManager em;
public void editUser(User user) {
...
}
}
#Service
public class UserService {
#Autowired
private UserDao userDao;
#Transactional
public void editUser(User user) {
...
}
}
#RestController
#RequestMapping(value = "/UserCtrl", produces = "application/json;charset=UTF-8")
public class UserCtrl {
private static final Logger logger = LogManager.getLogger(AppConfig.LOGGER_NAME);
#Autowired
private ApplicationContext appContext;
#RequestMapping(value = "/editUser")
public ActionResult editUser(#ModelAttribute User user) {
ActionResult rslt = new ActionResult();
try {
UserService userService = appContext.getBean(UserService.class);
userService.editUser(user);
rslt.setSuccess(true);
} catch (Exception ex) {
logger.error("editUser", ex);
rslt.setSuccess(false);
}
return rslt;
}
}
This code works correctly.
One thing I cannot understand is how aop proxy of UserService bean starts transaction, when EntityManager is injected only in UserDao class. Can somebody explain me how this works and how EntityManager lifecycle is managed by spring framework ?
Any help appreciated.
The TransactionManager is responsible for managing the transaction boundaries
in Spring Framework.
When you annotate the business method with #Transactional you are instrumenting the method with an aspect.
Before executing the method, this aspect interact with TransactionManager which decides if it is necessary to create a new Transaction or use a preexisting one. In the case of create a new Transaction, a new Entity Manager is created from EntityManagerFactory and is associated to the current thread alonside with the Transaction.
The EntityManager is responsible to iterate with the database. When you inject it with #PersistenceContext, Spring injects an proxy to the EntityManager.
Whenever an operation is executed in the EntityManager proxy it looks the EntityManager associated with the thread.
Based on the snippet of code you provide you are using spring with some implementation of JPA you are using J2EE container for your web application and bases on my guess I think this is good article about how things works.
Basic concepts :
EntityManager - A class that manages the persistent state(or lifecycle) of an entity.
Persistence Unit - is a named configuration of entity classes.
Persistence Context - is a managed set of entity instances. The entities classes are part of the Persistence Unit configurations.
Managed Entities - an entity instance is managed if it is part of a persistence context and that Entity Manager can act upon it.
Based on this article you use Container Managed EntityManager by using #PersistenceContext
When a container of the application(be it a Java EE container or any other custom container like Spring) manages the lifecycle of the Entity Manager, the Entity Manager is said to be Container Managed. The most common way of acquiring a Container Managed EntityManager is to use #PersistenceContext annotation on an EntityManager attribute.
And the injection is like this :
A Transaction Scoped Entity Manager is returned whenever a reference created by #PersistenceContext is resolved.
and
Every time an operation is invoked on an Entity Manager, the container proxy(the container creates a proxy around the entity manager while instantiating it ) checks for any existing Persistence Context on the JTA Transaction. If it finds one, the Entity Manager will use this Persistence Context. If it doesnt find one, then it will create a new Persistence Context and associates it with the transaction.
So the lifecycle of of entity manager is managed by your container and
we work with an instance of EntityManager, the only role of EntityManager is to determine the lifetime of the Persistence Context. It plays no role in dictating how a Persistence Context should behave. To reiterate, Persistence Context is a managed set of Entity instances. Whenever a transaction begins, a Persistence Context instance gets associated with it. And when a Transaction ends(commits for example), the Persistence Context is flushed and get disassociated with the transaction.
In the nutshell container instantiate your EntityManager by the EntityManagerFactory you provide and manage it's lifecycle but inject the proxy of EntityManager for you. The TransactionManager is responsible for creating, commiting and ... tranactions based on annotations you provide ,whenever it begins the transaction the PersistancecContext associated with it and when the tranasction ends it commites the state of PersistenceContext. The EntityManager works with PersistenceContext and if it's not provided the EntityManager creates one. The EntityManager is threadsafe as long as it holds no state and the state (PersistenceContext) is attached to current tranasction.
Hope this helps.

Spring - "late binding" autowired beans

Let's say I have the following dependencies:
#Configuration
public class MyCfg {
// ...
#Bean
public Session session() {
return sessionFactory().getCurrentSession();
}
}
#Repository
#Transactional
public class MyRepo {
#Autowired
private Session session;
}
sessionFactory() is set up properly. If I inject SessionFactory instead of Session, it works just fine. However, if try and inject Session, it dies with an exception on container bootstrap because there is no session bound to thread.
Since the repository is #Transactional, I know that at run time there will be a session. How can I make it work, so that it injects AOP-initialized Session at run time, but does not try and resolve it when the repo is instantiated?
I would take a look at this bit of Spring documentation regarding bean scopes. Near the bottom they show how to use the #Scope annotation, which you will want to apply to your session() method in MyCfg. It sounds like you would want to use the 'request' value, which will create a new instance of this bean for each HTTP request coming in.
I will also suggest taking a look at the <aop:scoped-proxy/> element for configuration. It is mentioned a couple times in the documentation, and may be useful for what you are trying to do.
This approach will get you into a lot of trouble. Instead of injecting a Session, which you now automatically scopes as a singleton, you should inject the SessionFactory instead. Instances of Session acquired within a #Transactional annotated method will adhere to those transaction rules, eg:
#Transactional(readonly=true)
public List<Person> getPersons() {
Session session = sessionFactory.getCurrentSession();
//find those darn people.
}
#Autowired
private SessionFactory sessionFactory;

Categories

Resources