I make a POC with spring-boot-starter-data-jpa and spring-boot-starter-activemq. I would like to push the jms message on the broker (activeMQ) when the jpa transaction was commited.
My code :
UtilsateurService with have the "main" transaction:
#Service
public class UtilisateurService {
#Autowired
private UtilisateurRepository utilisateurRepository;
#Autowired
private SendMessage sendMessage;
#Transactional(rollbackOn = java.lang.Exception.class)
public Utilisateur create(Utilisateur utilisateur) throws Exception {
final Utilisateur result = utilisateurRepository.save(utilisateur);
sendMessage.send("creation utilisateur : " + result.getId());
throw new Exception("rollback");
//return result;
}
}
The SendMessage class witch "manage" Jms message:
#Component
public class SendMessage {
#Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
#Value("${jms.queue.destination}")
private String destinationQueue;
public void send(String msg) {
this.jmsMessagingTemplate.convertAndSend(destinationQueue, msg);
}
}
My main class :
#SpringBootApplication
#EnableJms
#EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The JMS message was push on the activeMq broker before exception was throw. So I don't have "rollback" on the broker.
How can I configure to have xa transaction running?
is your jmsTemplate Transacted ?
jmsTemplate.setSessionTransacted(true);
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jms/support/JmsAccessor.html#setSessionTransacted-boolean-
public void setSessionTransacted(boolean sessionTransacted)
Set the transaction mode that is used when creating a JMS Session.
Default is "false". Note that within a JTA transaction, the parameters
passed to create(Queue/Topic)Session(boolean transacted, int
acknowledgeMode) method are not taken into account. Depending on the
Java EE transaction context, the container makes its own decisions on
these values. Analogously, these parameters are not taken into account
within a locally managed transaction either, since the accessor
operates on an existing JMS Session in this case.
Setting this flag to "true" will use a short local JMS transaction
when running outside of a managed transaction, and a synchronized
local JMS transaction in case of a managed transaction (other than an
XA transaction) being present. This has the effect of a local JMS
transaction being managed alongside the main transaction (which might
be a native JDBC transaction), with the JMS transaction committing
right after the main transaction.
http://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html
30.2.5 Transaction management
Spring provides a JmsTransactionManager that manages transactions for
a single JMS ConnectionFactory. This allows JMS applications to
leverage the managed transaction features of Spring as described in
Chapter 17, Transaction Management. The JmsTransactionManager performs
local resource transactions, binding a JMS Connection/Session pair
from the specified ConnectionFactory to the thread. JmsTemplate
automatically detects such transactional resources and operates on
them accordingly.
In a Java EE environment, the ConnectionFactory will pool Connections
and Sessions, so those resources are efficiently reused across
transactions. In a standalone environment, using Spring’s
SingleConnectionFactory will result in a shared JMS Connection, with
each transaction having its own independent Session. Alternatively,
consider the use of a provider-specific pooling adapter such as
ActiveMQ’s PooledConnectionFactory class.
JmsTemplate can also be used with the JtaTransactionManager and an
XA-capable JMS ConnectionFactory for performing distributed
transactions. Note that this requires the use of a JTA transaction
manager as well as a properly XA-configured ConnectionFactory! (Check
your Java EE server’s / JMS provider’s documentation.)
Reusing code across a managed and unmanaged transactional environment
can be confusing when using the JMS API to create a Session from a
Connection. This is because the JMS API has only one factory method to
create a Session and it requires values for the transaction and
acknowledgment modes. In a managed environment, setting these values
is the responsibility of the environment’s transactional
infrastructure, so these values are ignored by the vendor’s wrapper to
the JMS Connection. When using the JmsTemplate in an unmanaged
environment you can specify these values through the use of the
properties sessionTransacted and sessionAcknowledgeMode. When using a
PlatformTransactionManager with JmsTemplate, the template will always
be given a transactional JMS Session.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html#jms-tx
Hassen give the solution. So I change the SendMessage class to :
#Component
public class SendMessage {
private final JmsMessagingTemplate jmsMessagingTemplate;
#Value("${jms.queue.destination}")
private String destinationQueue;
#Autowired
public SendMessage(JmsMessagingTemplate jmsMessagingTemplate) {
this.jmsMessagingTemplate = jmsMessagingTemplate;
this.jmsMessagingTemplate.getJmsTemplate().setSessionTransacted(true);
}
public void send(String msg) {
this.jmsMessagingTemplate.convertAndSend(destinationQueue, msg);
}
}
Related
In my java process I'm connecting to MySql using the following spring configuration:
#Configuration
#EnableTransactionManagement
#PropertySources({ #PropertySource("classpath:/myProperties1.properties"), #PropertySource("classpath:/myProperties2.properties") })
public class MyConfiguration {
#Autowired
protected Environment env;
/**
* #return EntityManagerFactory for use with Hibernate JPA provider
*/
#Bean(destroyMethod = "destroy")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setPersistenceUnitManager(persistenceUnitManager());
return em;
}
/**
*
* #return jpaVendorAdapter that works in conjunction with the
* persistence.xml
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.valueOf(env.getProperty("jpa.database")));
vendorAdapter.setDatabasePlatform(env.getProperty("jpa.dialect"));
vendorAdapter.setGenerateDdl(env.getProperty("jpa.generateDdl", Boolean.class, false));
vendorAdapter.setShowSql(env.getProperty("jpa.showSql", Boolean.class, false));
return vendorAdapter;
}
#Bean
public PersistenceUnitManager persistenceUnitManager() {
DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
pum.setPackagesToScan("com.app.dal");
pum.setDefaultPersistenceUnitName("my-pu");
pum.setPersistenceXmlLocations("classpath:/META-INF/persistence.xml");
pum.setDefaultDataSource(dataSource());
return pum;
}
#Bean(destroyMethod = "close")
public DataSource dataSource() {
Properties dsProps = new Properties();
dsProps.put("driverClassName", env.getProperty("hikari.driverClassName"));
dsProps.put("username", env.getProperty("hikari.username"));
dsProps.put("password", env.getProperty("hikari.password"));
dsProps.put("jdbcUrl", env.getProperty("hikari.source.data.jdbcUrl"));
dsProps.put("connectionTimeout", env.getProperty("hikari.connectionTimeout", Integer.class));
dsProps.put("idleTimeout", env.getProperty("hikari.idleTimeout", Integer.class));
dsProps.put("maxLifetime", env.getProperty("hikari.maxLifetime", Integer.class));
dsProps.put("maximumPoolSize", env.getProperty("hikari.maximumPoolSize.rtb.source", Integer.class));
dsProps.put("leakDetectionThreshold", env.getProperty("hikari.leakDetectionThreshold", Integer.class));
dsProps.put("jdbc4ConnectionTest", env.getProperty("hikari.jdbc4ConnectionTest", Boolean.class));
HikariConfig config = new HikariConfig(dsProps);
HikariDataSource ds = new HikariDataSource(config);
return ds;
}
#Bean(name = "sourceTxMgr")
public PlatformTransactionManager sourceDatatransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName("my-pu");
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public PersistencyManager persistencyManager() {
return new JpaPersistencyManager();
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
The Entity-Manager is injected to the data access layer by the container:
#PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;
And my public business logic methods are annotated with the #Transactional annotation.
As far as I understand the container is responsible for ensuring that the entity-manager returns connections to the pool (in my case HikariCP) once a transaction is done but I did not find any official documentation that describes how the connections are managed. Can anyone explain it to me or provide a good reference that can explain when exactly connections are returned to the pool when using such a configuration?
UPDATE:
The best related info I could come up with so far (taken from here):
The persistence context proxy that implements EntityManager is not the only component needed for making declarative transaction management work. Actually three separate components are needed:
The EntityManager Proxy itself
The Transactional Aspect
The Transaction Manager
Let's go over each one and see how they interact.
The Transactional Aspect
The Transactional Aspect is an 'around' aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor.
The Transactional Aspect has two main responsibilities:
At the 'before' moment, the aspect provides a hook point for determining if the business method about to be called should run in the scope of an ongoing database transaction, or if a new separate transaction should be started.
At the 'after' moment, the aspect needs to decide if the transaction should be committed, rolled back or left running.
At the 'before' moment the Transactional Aspect itself does not contain any decision logic, the decision to start a new transaction if needed is delegated to the Transaction Manager.
The Transaction Manager
The transaction manager needs to provide an answer to two questions:
should a new Entity Manager be created?
should a new database transaction be started?
This needs to be decided at the moment the Transactional Aspect 'before' logic is called. The transaction manager will decide based on:
the fact that one transaction is already ongoing or not
the propagation attribute of the transactional method (for example REQUIRES_NEW always starts a new transaction)
If the transaction manager decides to create a new transaction, then it will:
create a new entity manager
bind the entity manager to the current thread
grab a connection from the DB connection pool
bind the connection to the current thread
The entity manager and the connection are both bound to the current thread using ThreadLocal variables.
They are stored in the thread while the transaction is running, and it's up to the Transaction Manager to clean them up when no longer needed.
Any parts of the program that need the current entity manager or connection can retrieve them from the thread. One program component that does exactly that is the EntityManager proxy.
It's not complicated at all.
First, you need to understand that the Spring transaction manager is only a transaction management abstraction. In your case, the actual transactions happen at the JDBC Connection level.
All #Transactional service method calls are intercepted by the TransactionInterceptor Aspect.
The TransactionIntreceptor delegates transaction management to the current configured
AbstractPlatformTransactionManager implementation (JpaTransactionManager in your case).
JpaTransactionManager will bind the current running Spring transaction to an EntityManager, so all DAOs participating in the current transaction share the same Persistence Context.
JpaTransactionManager simply uses the EntityManager Transaction API for controlling transactions:
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
tx.commit();
The JPA Transaction API simply delegates the call to the underlying JDBC Connection commit/rollback methods.
When the transaction is done (commit/rollback), the org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction calls:
transactionCoordinator().getTransactionContext().managedClose();
which triggers a Hibernate Session (Entity Manager) close.
The underlying JDBC connection is therefore triggered to be closed as well:
jdbcCoordinator.close();
Hibernate has a logical JDBC connection handle:
#Override
public Connection close() {
LOG.tracev( "Closing JDBC container [{0}]", this );
if ( currentBatch != null ) {
LOG.closingUnreleasedBatch();
currentBatch.release();
}
cleanup();
return logicalConnection.close();
}
The logical connection delegates the close call to the currently configured connection provider (DataSourceConnectionProvider in your case), which simply calls the close method on the JDBC connection:
#Override
public void closeConnection(Connection connection) throws SQLException {
connection.close();
}
Like any other connection pooling DataSource, the JDBC connection close simply returns the connection to the pool and doesn't close the physical database connection. That's because the connection pooling DataSource returns a JDBC Connection proxy that intercepts all calls and delegates the closing to the connection pool handling logic.
Note that for RESOURCE_LOCAL transactions, you should also set the hibernate.connection.provider_disables_autocommit property if the autocommit check was disabled by the connection pool. This way, the database connections are going to be acquired lazily prior to executing a SQL query or flushing the Persistence Context.
I am thinking to create a Spring Integration Spring Boot application to
1-Poll messages from a DB
2-Do some processing on it
3-Publish messages to EMS Queue
using Atomikos for Transaction management. My question is: If the above configuration will be transactional with all the required JTA configurations done? Also I have read somewhere, if multiple threads are created in Spring Integration,e.g,using a Splitter, then the context won't to transactional. How to overcome this?
If you configure the poller as transactional, the flow will run in a transaction, as long as you don't hand off to another thread (via an ExecutorChannel or QueueChannel channel, for example).
Adding a splitter will not break the transaction boundary as each split will be processed on the same thread.
Spring Integration has different requirements for transactions, to Do so you need to pass a transaction manager in the poller metaData, for example:
#Bean
public PollerMetadata pollerMetadata() throws NamingException {
return Pollers.fixedDelay(Long.valueOf(env.getProperty("poller.interval")))
.transactional(**transactionManager**).get();
}
With
#Autowired
private PlatformTransactionManager **transactionManager**;
And putting :
#InboundChannelAdapter(channel = "jpaInputChannel", poller = #Poller(value = "**pollerMetadata**"))
I wonder how to use JMS transactions correctly inside an EJB container. I found this code, that sends messages using JMS in a stateless bean:
#Stateless
public class SendEventsBean {
private static final Logger log = Logger.getLogger(SendEventsBean.class);
#Resource(mappedName = "jms/MyConnectionFactory")
private ConnectionFactory jmsConnectionFactory;
#Resource(mappedName = "jms/myApp/MyQueue")
private Queue queue;
public void sendEvent() {
Connection jmsConnection = null;
try {
connection = jmsConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
MyObj obj = new MyObj(1, "Foo");
ObjectMessage myObjMsg = session.createObjectMessage(obj);
producer.send(myObjMsg);
} catch (JMSException jmxEx) {
log.error("Couldn't send JMS message: ", jmsEx);
}finally{
if (jmsConnection != null) {
try {
jmsConnection.close();
}catch(JMSException ex) {
log.warn("Couldn't close JMSConnection: ", ex);
}
}
}
}
(from When should I close a JMS connection that was created in a stateless session bean? )
At default the transactions are container managed with transaction attribute 'required'. Suppose a client calls sendEvent() directly, so that the transaction starts at the beginning and ends at the end of sendEvent() (-> a commit is executed at the very end of the method).
Isn't it wrong to close the connection (jmsConnection.close()) BEFORE the commit occurs at the very end of the method?
Furthermore I'm wondering how setting the transactional attribute and setting true/false at createSession() interacts.
Does it make sense setting createSession(true,...) if there is already a transaction started by the container (using container managed transactions)? Does this create a new transaction just for JMS messages (and not for DB also) inside the JTA transaction?
And with createSession(false, ...) am I right, that messages are nevertheless transactional because of the transaction started by the container?
Isn't it wrong to close the connection (jmsConnection.close()) BEFORE the commit occurs at the very end of the method?
No. Closing connections has got nothing to do with commit within a JTA transaction (which is the case here, it being an ejb with CMT). It is just proper and essential cleanup. Note these are connections returned by the container and the underlying transaction manager knows how to work with the resource to commit the transactions. Same goes for JDBC connections as well.
Does it make sense setting createSession(true,...) if there is already a transaction started by the container (using container managed transactions)?
For Weblogic, you should definitely be using non transacted sessions. But what is important to use XA connection factories for your JMS connections.
http://docs.oracle.com/cd/E11035_01/wls100/jms/trans.html#wp1031645
http://www.informit.com/articles/article.aspx?p=26137&seqNum=8
However articles related to JBOSS suggests setting the createSession(true...) as a good practise even within a CMT ejb
https://developer.jboss.org/thread/213629?tstart=0&_sscc=t
http://www.coderanch.com/t/606786/EJB-JEE/java/EJB-CMT-sending-JMS-message
Irrespective of the setting, JCA/XA based connection factories have to be used compulsorily.
And with createSession(false, ...) am I right, that messages are
nevertheless transactional because of the transaction started by the
container?
No. As mentioned above, you will have to use XA connection factories.
I'm currently experiencing an unexpected behaviour with the Spring transaction manager: the same transaction is assigned to multiple threads.
The scenario is a CXF webservice running in a Tomcat server. The service itself is a Spring bean (singleton). When sending many parallel requests (in my test I used 10) to the server it sometimes happens that two requests (running in different threads) work with the same transaction. Since the logic is designed to use one transaction per request this behaviour causes a faulty behaviour of the application.
Below my service class:
package ...clientsupport_v1;
import...
#WebService(targetNamespace = ...)
#Transactional(rollbackFor = MyException.class)
public class ClientSupportFacade extends AbstractServiceFacade implements ClientSupportService {
private static final Logger LOG = LoggerFactory.getLogger(ClientSupportFacade.class);
#Autowired
private HibernateTransactionManager transactionManager;
#Autowired
private ClientOrderImporter clientOrderImporter;
#Override
public Advice receiveClientPreAdvice(User user, PreAdvice preAdvice) throws MyException {
LOG.debug("PreAdvice: " + preAdvice + ", Transaction: " + getTransactionId() + ", Thread: "
+ Thread.currentThread().getName());
...
return advice;
}
private int getTransactionId() {
return transactionManager.getSessionFactory().getCurrentSession().getTransaction().hashCode();
}
}
receiveClientPreAdvice is the method exposed as service operation.
When calling with multiple threads I get the following log lines:
2013-10-29 13:59:25.135 DEBUG e.h.p.s.c.c.ClientSupportFacade.receiveClientPreAdvice:42 - PreAdvice: PreAdvice [barcode=90000000002161, ...], Transaction: 420660542, Thread: http-bio-8080-exec-9
2013-10-29 13:59:25.135 DEBUG e.h.p.s.c.c.ClientSupportFacade.receiveClientPreAdvice:42 - PreAdvice: PreAdvice [barcode=90000000002163, ...], Transaction: 420660542, Thread: http-bio-8080-exec-8
As you can see two different objects have been received and two different threads are running. Yet the transaction is the same.
How can this happen? And - even more important - how can it be avoided?
From HibernateTransactionManager javadoc:
Binds a Hibernate Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory ... This transaction manager is appropriate for applications that use a single Hibernate SessionFactory for transactional data access, ...
It may be helpful to post your hibernate configuration but from your code, I can see you are logging getTransaction().hashCode() as Transaction ID assuming they refer to the same transaction object while hashCode() is not required to return different values for different objects. There can be 2 distinct transactions in progress that both have the same hash-code value.
We have a simple stateless EJB timer that gets an instance of a spring service injected into it. The spring service has a method marked as transactional. When the EJB uses Transaction Management Type CONTAINER the spring service call results in an IllegalStateException: Operation not allowed. So we set the EJB Transaction Management Type to be BEAN and everything works as advertised. Is this normal? I was under the impression that the spring transaction management would join the CMT.
example EJB
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
#Interceptors(SpringBeanAutowiringInterceptor.class
public class TimerService {
#Autowired
IHelloService helloService;
#Schedule(second="*/1", minute="*",hour="*", persistent=false)
public void doWork(){
helloService.hello();
}
}
example spring service
#Service
public class HelloService implements IHelloService {
#Transactional
public void hello(){
}
}
Probably the problem can be fixed, if you would define propagation level for Spring transaction as SUPPORTS (default is REQUIRED). I do not remember exact value for default settings in EJB, but it seems like EJB method is not starting transaction (propagation is DEFAULT), but 'hello' tries to do so and that is causing conflict.