I'm using Spring 3.1.2 with Hibernate 4.
I have a DAO implementation class MyDaoImpl annotated with #Repository so that exception translation is enabled. I have a service class MyService annotated with #Transactional as follows:
public class MyService implements IMyService {
private MyDao myDao;
#Autowired
public void setMyDao(MyDao dao) {
this.myDao = dao;
}
#Override
#Transactional
public void createA(String name)
{
A newA = new A(name);
this.myDao.saveA(newA);
}
}
I've wrote a unit tests class MyServiceTest as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:beans.xml" })
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class MyServiceTest implements IMyServiceTest {
private IMyService myService;
private SessionFactory sessionFactory;
#Autowired
public void setMyService(IMyService myService)
{
this.myService = myService;
}
#Autowired
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
#Test
#Override
public void testCreateA()
{
//Assume that there is already a row of table A with the name "A1"
//so I expect to get a Spring DataAccessException (or subtypes)
//when the session is flushed
this.myService.createA("A1");
this.sessionFactory.getCurrentSession().flush();
//asserts
}
}
When I run the test, I get a Hibernate specific exception ConstraintViolationException. I've found on the forum that this is because the translation system takes place outside the transaction, so in this case after testCreateA() returns. I don't know if this is the real cause, but if it is, it means that I can't test that the translation works for my DAOs. One solution would be to remove the #Transactional annotations from my unit tests, but I would no benefit from the rollback feature.
What are your recommendations?
EDIT: I've added the SessionFactory declared in my context to the test class, so that I can access the current session for flushing.
Some additional explanations: In this case, I get the exception when the session is flushed (which is inside the transaction). I flush the session in order to avoid false positives as it is explained in the docs. Also, since the default propagation is REQUIRED, the testCreateA() transaction is also used for the call to createA(), so the changes are not flushed (generally) until testCreateA() returns.
Have you added PersistenceExceptionTranslationPostProcessor bean defination? Like
<!--
Post-processor to perform exception translation on #Repository classes
(from native exceptions such as JPA PersistenceExceptions to
Spring's DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
From Spring doc.
Bean post-processor that automatically applies persistence exception
translation to any bean that carries the #Repository annotation, adding
a corresponding PersistenceExceptionTranslationAdvisor to the exposed
proxy (either an existing AOP proxy or a newly generated proxy that
implements all of the target's interfaces).
Translates native resource exceptions to Spring's DataAccessException
hierarchy. Autodetects beans that implement the
PersistenceExceptionTranslator interface, which are subsequently asked
to translate candidate exceptions
Related
I'm trying to understand, how does injecting of EntityManger in Spring bean work.
I have bean
#Service
#RequiredArgsConstructor(onConstructor_ = #Autowired)
public class TestUpdateService {
#PersistenceContext
private EntityManager entityManager;
private final OrderRepository orderRepository;
#Transactional
public void doWork(Long id) {
Order order = orderRepository.findById(id).get();
entityManager.detach(order);
orderRepository.save(order);
}
}
And have a couple of questions:
Am I right that for every call of my transactional method doWork new instance of EntityManager will be created? I read (or mb misunderstood) on stackoverflow that entity manager annotated with #PersistanceContext creates own EntityManager for each transaction.
If it is so, does #Autowired work the same way?
If it isn't so, then how do these both annotations work?
I am trying to update a node on Neo4J, but what ends up happening is that it creates a duplicate Node. I read that the update has to be in a single transaction and I added #Transactional, but still same result. Here is what I have. I tried the approach of reading and deleting the old node, and saving the new one and it appears to be working. But, I think that is not the right approach. Why the #Transactional annotation not working. Thank you.
#EnableNeo4JRepositories(com.example.graph.repo)
#EnableTransactionManagement
#org.springframework.contect.annotation.Configuration
public class Neo4JConfig {
#Bean
public Configuration configuration() {
Configuration cfg = new Configuration();
cfg.driverConfiguration()
.setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver")
.setURI("http://neo4j:neo4j#localhost:7474");
return cfg;
}
#Bean
public SessionFactory sessionFactory() {
return new SessionFactory(configuration(), "com.example");
}
#Bean
public Neo4jTransactionManager transactionManager() {
return new Neo4JTransactionManager(sessionFactory());
}
}
#Service
public class UserService{
#Autowired
UserRepository userRepository;
#Transactional
public void updateUser(User user) {
User existingUser = userRepository.getExistingUser(user.getUserName());
if(existingUser != null ) {
user.setSomeValue(existingUser.getSomeValue());
userRepository.save(user);
}
}
}
Spring AOP uses JDK Proxy mechanism by default. It means that you must invoke #Transactional method via interface method.
So you should split your service into interface UserService and implementation (say UserServiceImpl), autowire the interface into the code where you currently autowire the impementation, and then invoke transactional method via interface.
P.S. Another approach is to force Spring to use CGLIB as long as this mechanism is not limited to interfaces. More details for both mechanisms https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html
I am writing an integration test using SpringJUnit4ClassRunner.
I have a base class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration({ /*my XML files here*/})
#Ignore
public class BaseIntegrationWebappTestRunner {
#Autowired
protected WebApplicationContext wac;
#Autowired
protected MockServletContext servletContext;
#Autowired
protected MockHttpSession session;
#Autowired
protected MockHttpServletRequest request;
#Autowired
protected MockHttpServletResponse response;
#Autowired
protected ServletWebRequest webRequest;
#Autowired
private ResponseTypeFilter responseTypeFilter;
protected MockMvc mockMvc;
#BeforeClass
public static void setUpBeforeClass() {
}
#AfterClass
public static void tearDownAfterClass() {
}
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(responseTypeFilter).build();
}
#After
public void tearDown() {
this.mockMvc = null;
}
}
Then I extend it and create a test using mockMvc:
public class MyTestIT extends BaseMCTIntegrationWebappTestRunner {
#Test
#Transactional("jpaTransactionManager")
public void test() throws Exception {
MvcResult result = mockMvc
.perform(
post("/myUrl")
.contentType(MediaType.APPLICATION_XML)
.characterEncoding("UTF-8")
.content("content")
.headers(getHeaders())
).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_XML))
.andExpect(content().encoding("ISO-8859-1"))
.andExpect(xpath("/*[local-name() ='myXPath']/")
.string("result"))
.andReturn();
}
In the end of the flow, an entity is saved into DB. But the requirement here is that is should be done asynchronously. So consider this method is called:
#Component
public class AsyncWriter {
#Autowired
private HistoryWriter historyWriter;
#Async
public void saveHistoryAsync(final Context context) {
History history = historyWriter.saveHistory(context);
}
}
Then HistoryWriter is called:
#Component
public class HistoryWriter {
#Autowired
private HistoryRepository historyRepository;
#Transactional("jpaTransactionManager")
public History saveHistory(final Context context) {
History history = null;
if (context != null) {
try {
history = historyRepository.saveAndFlush(getHistoryFromContext(context));
} catch (Throwable e) {
LOGGER.error(String.format("Cannot save history for context: [%s] ", context), e);
}
}
return history;
}
}
The problem with all this is that after test is done, History object is left in the DB. I need to make test transaction to rollback all changes in the end.
Now, what I've tried so far:
Remove #Async annotation. Obviously, this cannot be the solution, but was done to confirm rollback will be perform without it. Indeed, it is.
Move #Async annotation to HistoryWriter.saveHistory() method to have it in one place with #Transactional. This article https://dzone.com/articles/spring-async-and-transaction suggests it should work this way, but for me, no rollback is done after test.
Swap places of these two annotations. It does not give the desired result as well.
Does anyone have any idea how to force rollback of the DB changes made in asynchronous method?
Side notes:
Transaction configuration:
<tx:annotation-driven proxy-target-class="true" transaction- manager="jpaTransactionManager"/>
Async configuration:
<task:executor id="executorWithPoolSizeRange" pool-size="50-75" queue-capacity="1000" />
<task:annotation-driven executor="executorWithPoolSizeRange" scheduler="taskScheduler"/>
Does anyone have any idea how to force rollback of the DB changes made in asynchronous method?
That is unfortunately not possible.
Spring manages transaction state via ThreadLocal variables. A transaction started in another thread (e.g., the one created for your #Async method invocation) can therefore not participate in a transaction managed for the parent thread.
This means that the transaction used by your #Async method is never the same as the test-managed transaction which gets automatically rolled back by the Spring TestContext Framework.
Thus, the only possible solution to your problem is to manually undo the changes to the database. You can do this using JdbcTestUtils to execute an SQL script programmatically within an #AfterTransaction method, or you can alternatively configure an SQL script to be executed declaratively via Spring's #Sql annotation (using the after execution phase). For the latter, see How to execute #Sql before a #Before method for details.
Regards,
Sam (author of the Spring TestContext Framework)
I am trying to learn how to use transaction in java spring. I am a java novice so please bear with me :-) The unit test im trying to achieve below is testing:
rollback if a runtime exception is thrown.
The problem im having is a
java.lang.NullPointerException?
Ok here goes.. I have stripped out some code to help improve readability
TutorialTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class TutorialTest {
#Autowired
private Dao dao;
#Test
public void that_if_a_runtime_exception_is_thrown_transaction_rolledback(){
User u = new User();
u.setUsername("FAIL_TEST");
u.setPhone("0161");
u.setEmail("FAIL_TEST#gmail.com");
//dao.addUser(u); // -- this will insert if uncommented out so I know it works
OuterService os = new OuterService();
os.addUserThrowError(u);
}
}
OuterService.java
#ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class OuterService {
#Autowired
private Dao dao;
#Transactional
public void addUserThrowError(User user) throws RuntimeException{
dao.addUser(user); // gives me a java.lang.NullPointerException?
throw new RuntimeException("This should roll back DB entry");
}
}
Beans are declared in
AppConfig.java
#Configuration
#EnableTransactionManagement
#ComponentScan(value = {"com.training.spring.tx.tutorial.dao",
"com.training.spring.tx.tutorial.service"})
public class AppConfig {
public DataSource dataSource() {
// Create a BasicDataSource object and configure database
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/spring_training_tx");
dataSource.setUsername("training");
dataSource.setPassword("training");
return dataSource;
}
#Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(transactionManager().getDataSource());
}
}
First of all, to quote the javadoc of #ContextConfiguration
#ContextConfiguration defines class-level metadata that is used to
determine how to load and configure an ApplicationContext for
integration tests.
Consider how you are using it on OuterService. Does it seem right? Is OuterService meant to be used to load and configure an ApplicationCOntext for integration tests? Unless I'm missing something essential, the answer is: No.
So what is OuterService? It's some kind of service. You seem to want to use it as a bean. What is a bean? A bean is an object whose lifecycle is managed by Spring. This includes instantiation of the bean class, initialization of the object, post processing, and, finally, destruction of the object.
If you created the object like so
OuterService os = new OuterService();
then Spring is not involved. You created the object and there is no way for Spring to hook into that. You cannot therefore expect Spring to autowire its field
#Autowired
private Dao dao;
And since you haven't initialized the field, it remains null, which causes the NullPointerException.
So how do we get an OuterService bean, which is managed by Spring? You either declare a #Bean method for OuterService or you annotate OuterService with #Component or any of its specializations and component-scan the package it is in. You then inject the bean into any other bean that uses it. For example,
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class, loader=AnnotationConfigContextLoader.class)
public class TutorialTest {
#Autowired
private Dao dao;
#Autowired
private OuterService os;
You can then use that variable directly.
In a project of mine I'm trying to switch management of my persistence from application to container. I'm following these instructions: http://docs.oracle.com/javaee/6/tutorial/doc/gkhrg.html
I've read about EntityManager not being thread safe and just want to make sure that my setup is correct. My concern: http://weblogs.java.net/blog/2005/12/19/dont-use-persistencecontext-web-app.
I have a class that produces a persistence context.
#Singleton
public class JpaResourceProducer {
//The "pu" unit is defined with transaction-type="JTA"
#Produces
#PersistenceUnit(unitName = "pu")
#Database
EntityManagerFactory databasePersistenceUnit;
#Produces
#PersistenceContext(unitName = "pu")
#Database
EntityManager databaseEntityManager;
/* Alternative
#PersistenceContext(unitName = "pu")
private EntityManager em;
#Produces
#UserDatabase
public EntityManager create() {
return em;
}
public void close(#Disposes #Database EntityManager em) {
em.close();
}
*/
}
Then I have a jax-rs resource that injects a DAO.
#RequestScoped
#Path("/endpoint")
public class MyResource {
#Inject private Dao dao;
#GET
#Produces({MediaType.APPLICATION_JSON})
public Converter get() {
MyEntity entity = dao.find(1);
Converter converter = new Converter(entity);
return converter;
}
}
And finally a DAO where I inject the EntityManager.
#Singleton
public class JpaDao<T, K extends Serializable> implements Dao<T, K> {
protected Class<T> entityClass;
#Inject
#Database
EntityManager em;
public JpaDao() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
}
public T find(K id) {
return em.find(entityClass, id);
}
....
}
1. Is this a good setup in terms of thread safety and overall performance?
Bonus questions:
In the JpaResourceProducer I have an alternative setup for the EntityManager where I manually close the manager on dispose.
2. Is the container handling close of my EntityManager automatically?
The example from Oracle includes an EntityManagerFactory.
3. Do I really need an EntityManagerFactory when I'm using CMP?
What's your container? From annotations used I presume it is at least Java EE 6 Web Profile compatible, and in that case you're overcomplicating this.
The tutorial presents producting of multiple persistence units. Is this the case in your application? If not, plain injection into EJBs would be more relieable and less magical - I am also unsure about transactional behaviour of a singleton-produced persistence context.
Singleton JpaDAO means, there can be only one database operation at a time in your application, so this is serious performance bottleneck. It should be Stateless instead (and having its persistence context injected by traditional means).
In general, implement your DAOs and Business logic, even your JAX-RS endpoints as session EJBs, and let container handle concurrency and transactions properly.
Also use other sources of examples, e. g. Adam Bien's JavaEE Patterns.