Transaction Managment Spring framework - java

While learning spring framework I'm currently on the transaction management topic and while I'm not 100% against using xml I've been trying to do everything using annotations. Along comes this transaction management thing and the instructor just plops it right into the xml file and references a DataSource bean which I created with annotations
... BELOW ...
package com.udemy.learning.main;
import java.sql.SQLException;
import javax.sql.DataSource;
import javax.annotation.*;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component("connect")
public class Connections extends BasicDataSource{
#Value("${jdbc.user}")
private String username;
#Value("${jdbc.password}")
private String password;
#Value("${jdbc.driver}")
private String driverClassName;
#Value("${jdbc.url}")
private String url;
public DataSource connect() throws SQLException{
super.setDriverClassName(this.driverClassName);
super.setUrl(this.url);
super.setUsername(this.username);
super.setPassword(this.password);
return super.createDataSource();
}
#PreDestroy
public void close() {
try {
super.close();
System.out.println("Connection closed");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
So in an attempt to do everything with code and annotations I created a TransactionManager class and just used the #Component annotation above it. I'm sure you can imagine what that looks like so I won't drop that here, plus I'm thinking it's rather elementary looking anyway.
So anyway, long question short...
Is there a way to do this transaction management configuration the way I have attempted? rather than strictly xml? My attempt ended me up with an error like the following...
Bean named 'transactionManager' must be of type [org.springframework.transaction.PlatformTransactionManager], but was actually of type [com.udemy.learning.main.TransactionManager]

You need your bean to be of type PlatformTransactionManager so extend PlatformTransactionManager in your TransactionManager class. The exception is occuring because Spring is expecting any Transaction Manager to be a subclass of the PlatformTransactionManager

Related

#Transactional not committing (Spring Mybatis)

I have below Service:
#Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public void test(String idEntity) throws BaseException
{
getCustomerInformationDAO().updatetm(idEntity);
}
This service has been marked as #Service annotation.
I am calling this service from a controller.
#RequestMapping(value="/test", method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
#Override
public void test(#RequestParam("idEntity") String idEntity) throws BaseException
{
monolithicService.test(idEntity);
}
Below Dao(this has been marked as #Repository) method:
#Override
public void updatetm(String idEntity) throws BaseException
{
updateRecord( "customerinformation-update.updatelfcentitylDt", idEntity );
}
Transaction manager has been marked as
<tx:annotation-driven transaction-manager="transactionManager" />.
With above changes, it doesnt commit the transaction, even if it is successful.
Can anyone help me with this...
I dealt with a similar issue for a whole day.
Just when I was borderline with insanity, I found out that when you use #Transactional in a test the rules are different: by default, your changes are rolled back.
Quick solution: add the annotation #Commit to your method, i.e.:
#Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
#Commit
public void test(String idEntity) throws BaseException
{
getCustomerInformationDAO().updatetm(idEntity);
}
You can read some details in the following text:
One common issue in tests that access a real database is their effect on the state of the persistence store. Even when you use a development database, changes to the state may affect future tests. Also, many operations — such as inserting or modifying persistent data — cannot be performed (or verified) outside of a transaction.
The TestContext framework addresses this issue. By default, the framework creates and rolls back a transaction for each test. You can write code that can assume the existence of a transaction. If you call transactionally proxied objects in your tests, they behave correctly, according to their configured transactional semantics. In addition, if a test method deletes the contents of selected tables while running within the transaction managed for the test, the transaction rolls back by default, and the database returns to its state prior to execution of the test. Transactional support is provided to a test by using a PlatformTransactionManager bean defined in the test’s application context.
If you want a transaction to commit (unusual, but occasionally useful when you want a particular test to populate or modify the database), you can tell the TestContext framework to cause the transaction to commit instead of roll back by using the #Commit annotation.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testing
You probably have two data sources using MapperScan that might be messing up a mybatis config. You need to add SqlSessionFactory and SqlSessionTemplate as mentioned here http://mybatis.org/spring/getting-started.html.
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
#Configuration
#Slf4j
#MapperScan(value = "com.abc.xyx.aaa", sqlSessionTemplateRef = "PrimarySessionTemplate")
public class MyBatisPrimaryConfig {
#Bean(name = "PrimarySessionFactory")
#Primary
public SqlSessionFactory sessionFactory(#Qualifier("PrimaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
#Bean(name = "PrimarySessionTemplate")
#Primary
public SqlSessionTemplate primarySessionTemplate(#Qualifier("PrimarySessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

Manual bean registration and configuration order

I had an issue with spring's #Order annotation, it seems i can't get it to work in my application. Hence I managed to create a test class that imitates the very same behaviour which #Order does not have any effect on my components. The following test fails to run because of lack of bean typed javax.sql.Datasource:
package com.so;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class TestSpring {
public static void main(String[] args) {
Class<?>[] classes = new Class[]{AConf.class, ADAO.class, AService.class, RepoConf.class} ;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
}
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 100)
public static class AConf {
#Autowired
AService aService;
}
#Repository
#Order(Ordered.LOWEST_PRECEDENCE)
public static class ADAO {
#Autowired
#Qualifier("myds")
DataSource dataSource;
}
#Service
#Order(Ordered.LOWEST_PRECEDENCE)
public static class AService {
#Autowired
ADAO adao;
#PostConstruct
public void init() {
System.out.println("service init");
}
}
// #Component does not have any effect
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public static class RepoConf {
#Autowired
BeanFactory beanFactory;
#PostConstruct
public void init() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
configurableBeanFactory.registerSingleton("myds", new AbstractDataSource() {
#Override
public Connection getConnection() throws SQLException {
return null;
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
});
}
}
}
Manual bean registration has risks as stated here: https://stackoverflow.com/a/11751503/1941560, although I cannot find out in which circumtances that #Order annotation works. For above configuration of application I expect the execution order like; RepoConf, AConf, ADAO, AService.
A weird thing to notice is that when I changed the order of component classes declared to (with commencing array with RepoConf):
Class<?>[] classes = new Class[]{RepoConf.class, AConf.class, ADAO.class, AService.class};
or changed my AConf class to:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 100)
public static class AConf {
#Autowired
RepoConf repoConf; // must be declared before aService
#Autowired
AService aService;
}
application works as expected. Could someone explain that spring container's behaviour and how can I utilize #Order annotations?
springframework version I use is 4.2.1.RELEASE
Judging by the JavaDoc documentation for the #Order annotation, I don't think it is used to order bean creation:
NOTE: Annotation-based ordering is supported for specific kinds of
components only — for example, for annotation-based AspectJ aspects.
Ordering strategies within the Spring container, on the other hand, are
typically based on the Ordered interface in order to allow for
programmatically configurable ordering of each instance.
Consulting the Spring Framework documentation, the #Ordered annotation seems to be used for:
Ordering of instances when injected into a collection
Ordering the execution of event listeners
Ordering of #Configuration class processing, for example if you want to override a bean by name.
From the documentation, it does not look like #Order is intended for your use case.
However, from the Spring documentation, we can see there depends-on, which enforces that certain beans should be created before the bean being defined. This has a corresponding annotation #DependsOn.
It looks that your version of Spring Framework simply ignores the #Order annotation on configuration classes. There's no surprise here, because that annotation should only be used on classes that implement the Ordered interface. Moreover, I could never find any reference to it about configuration classes in Spring Framework reference documentation.
Anyway you are walking in terra incognita here. It is not described in official documentation, so it will work or not depending on implementation details. What happens here is (seeing the results):
the AnnotationConfigApplicationContext first instantiate the configuration classes and their beans in their declaration order
it then builds all the beans in their instantiation order
As you register the datasource myds at build time in its configuration class, it happens to be registered in time to be autowired in other beans when repoConf is built before any other beans using them. But this is never guaranteed by Spring Framework documentation and future versions could use a different approach without breaking their contract. Moreover, Spring does change initialization order to allow dependencies to be constructed before beans depending on them.
So the correct way here is to tell Spring that the ADAO bean depends on the configuration RepoConf. Just throw away all Order annotations which are no use here, and put a #DependsOn one. You code could be:
package com.so;
...
public class TestSpring {
public static void main(String[] args) {
Class<?>[] classes = new Class[]{AConf.class, ADAO.class, AService.class, RepoConf.class} ;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
}
#Configuration
public static class AConf {
#Autowired
AService aService;
}
#DependsOn("repoConf")
#Repository
public static class ADAO {
#Autowired
#Qualifier("myds")
DataSource dataSource;
}
#Service
public static class AService {
#Autowired
ADAO adao;
#PostConstruct
public void init() {
System.out.println("service init");
}
}
#Configuration("repoConf")
public static class RepoConf {
#Autowired
BeanFactory beanFactory;
#PostConstruct
public void init() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
configurableBeanFactory.registerSingleton("myds", new AbstractDataSource() {
#Override
public Connection getConnection() throws SQLException {
return null;
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
});
}
}
}
#DependsOn ensures that repoConf will be created before ADAO making the datasource available for dependency injection.

Container managed transcation (CMT) in hibernate + EJB

The hibernate documentation says:
With CMT, transaction demarcation is declared in session bean deployment descriptors, rather than performed in a programmatic manner.
but I can't find any complete example on how to do this.
This is what I have in mind my code should look like:
#Stateless
public class Dao{
#Inject // or some other annotation
private SessionFactory factory;
public void doDaoStuff(){
Object obj = factory.getCurrentSession().get(Entity.class, "Id");
// do something with obj
return;
}
}
It is free from all the boilerplate that hibernate has as transaction should be started, committed and roll backed by container.
So, Is it possible to do this? Although the documentation says that required declarations should be specified in bean deployment descriptors, doing it with annotations would be great.
In JavaEE environments Hibernate can use the CMT (Container Managed Transaction) strategy which will bind hibernate transactions with the underlying JTA transaction eliminating the need to manually begin, commit and rollback transactions. Example here.
However, there are problems:
This doesn't work in all Java EE Containers. Newer versions of Websphere are not supported and quoting from the source code of hibernate - WebSphere, however, is not a sane JEE/JTA container...
This restricts to one session one transaction idiom. Thus during the invocation of a EJB business method, there can be only one JTA or Hibernate transaction.
Luckily, using CDI, and some custom interceptors this can be solved and a lot of Hibernate boilerplate can be removed. I wrote a sample on github.
This approach creates a wrapper for Hibernate SessionFactory and provides most commonly used APIs. Using CDI, #RequestScoped Sessions are opened and closed automatically. Transactions are managed using an Interceptor. #RequestScoped ensures one Session per request so sessions aren't shared among multiple requests.
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
#RequestScoped
public class MySessionFactory implements SessionFactoryTemplate{
#Inject
private SessionFactory sessionFactory;// Inject after creating the singleton instance
private Session currentSession;
public Session openSession(){
return sessionFactory.openSession();
}
public Session getCurrentSession(){
if(currentSession == null){
currentSession = sessionFactory.openSession();
}
return currentSession;
}
public StatelessSession openStatelessSession() {
return sessionFactory.openStatelessSession();
}
#PreDestroy
private void closeSession(){
if(currentSession!=null && currentSession.isOpen()) {
currentSession.close();
}
}
}
This implementation is then injected in database layer and used to obtain sessions.
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.ares.cdi.hibernate.interceptors.Transactional;
public class Dao {
#Inject
private MySessionFactory sf;
public void get(int id){
sf.getCurrentSession().get(clazz,id);
}
#Transactional
public void add(Object entity){
sf.getCurrentSesion().add(entity);
}
}
Transactions are managed by TranscationManager interceptor and declared by #Transactional annotation.
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import org.ares.cdi.hibernate.sf.MySessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;
#Interceptor
#Transactional
public class TransactionManager {
#Inject
private MySessionFactory sessionFactory;
#AroundInvoke
public Object handleTransaction(InvocationContext context) throws Exception{
Session session = sessionFactory.getCurrentSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
return context.proceed();
}
catch(Exception e){
tx.rollback();
throw e;
}
finally{
if(tx.getStatus().equals(TransactionStatus.ACTIVE)){
try{
tx.commit();
}
catch(Exception e){
tx.rollback();
throw e;
}
}
}
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
#InterceptorBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface Transactional {
}
EJB methods are transactional by default.
You can tweak their behavior using the TransactionAttribute annotation.
You can read more about CMT here:
https://docs.oracle.com/javaee/7/tutorial/transactions003.htm#BNCIJ
https://docs.oracle.com/javaee/7/tutorial/transactions.htm#BNCIH
Your Typical example would be like--
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
#TransactionManagement(TransactionManagementType.CONTAINER)
#Stateless(..)
public class YourBean{
#TransactionAttribute(TransactionAttributeType.REQUIRED) // if in case you wanted to use 'existing' transaction
public void DoStuff(){
}
}
And in your server configurations you require below tag under <enterprise-beans>
<transaction-type>Container</transaction-type>

Spring Boot auto-configuration order

I want to create a Spring Boot auto-configuration class that (conditionally) creates a single bean A. The challenge however is, that this been has to be created before another bean B is created in one of Spring Boot's default auto-configuration classes. The bean B does not depend on A.
My first try was to use #AutoConfigureBefore. That didn't work the way I expected and judging from this comment by Dave Syer it shouldn't.
Some background: The aforementioned bean A alters a Mongo Database and this has to happen before MongoTemplate is created.
It turns out, what I want is to dynamically make instances of B depend on A. This can be achieved by using a BeanFactoryPostProcessor to alter the bean definitions of B beans.
public class DependsOnPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
beanFactory, B.class, true, false);
for (String beanName : beanNames) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
definition.setDependsOn(StringUtils.addStringToArray(
definition.getDependsOn(), "beanNameOfB");
}
}
}
This works with plain Spring, no Spring Boot required. To complete the auto-configuration I need to add the bean definition for the DependsOnPostProcessor to the configuration class that instantiates the bean A.
There is a new annotation #AutoConfigureOrder in Boot 1.3.0. Though it is unclear to me at least if this would still behave the same way as #AutoConfiugreBefore.
Refer to #hzpz 's answer, here is a example for create database before the auto-configuration of Hikari data source.
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
#Configuration
public class CreateDatabaseConfig {
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.hikari.username}")
private String username;
#Value("${spring.datasource.hikari.password}")
private String password;
#Value("${spring.datasource.hikari.catalog}")
private String catalog;
#Bean
public static BeanFactoryPostProcessor dependsOnPostProcessor() {
return beanFactory -> {
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
beanFactory, HikariDataSource.class, true, false);
for (String beanName : beanNames) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
definition.setDependsOn(StringUtils.addStringToArray(
definition.getDependsOn(), "createDatabaseConfig"));
}
};
}
#PostConstruct
public void init() {
try (
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement()
) {
statement.executeUpdate(
"CREATE DATABASE IF NOT EXISTS `" + catalog
+ "` DEFAULT CHARACTER SET = utf8mb4 COLLATE utf8mb4_unicode_ci;"
);
} catch (SQLException e) {
throw new RuntimeException("Create Database Fail", e);
}
}
}
More initialization for schema and data can be done with data.sql and schema.sql, see 85. Database Initialization
ps. I tried to CREATE DATABASE in schema.sql but failed, and the example above works :)

Spring Bean Injection in Liferay Scheduled Job

I have a scheduled job running in liferay 6.1.2 which requires spring injection.
Sample code -
import com.liferay.portal.kernel.messaging.Message;
import com.liferay.portal.kernel.messaging.MessageListener;
import com.liferay.portal.kernel.messaging.MessageListenerException;
import java.lang.reflect.InvocationTargetException;
public class ScheduledJob implements MessageListener {
#Autowired
private SomeService service;
#Override
public void receive(final Message msg) throws MessageListenerException {
try {
service.someMethod();
} catch (final IllegalAccessException e) {
} catch (final InvocationTargetException e) {
}
}
}
The injected service is not initialized through application context and is always null.
Auowiring works perfectly fine for other classes. Only fails for ScheduledJob.
Anyone knows the solution?
Thanks
I haven't try it my self but you can try to implement ApplicationContextAware interface by your ScheduledJob class and get injected beans as described here.
Create one class in another package and in that class autowire your service with the help of the constructor by putting #component annotation on class and #Autowire annotation on parameterized constructor where you will pass serviceobject in paramater.
In this class write one static method which will use your service.
Put this package name in componentscan tag in spring xml file.
Now in receive method you have to call that method with class name as method is static.

Categories