I try to do a global (distribute) transaction in Spring without Java EE (aplication server like JBoss) only with tomcat. There are two databases involved, the first is a PostgreSQL Database, the second is a MS SQLServer Database.
Is there a way to do it without using hibernate?
I tried it with the atomikos API, but I don't know how to do it without a hibernate session. I think it would to great to do it JDBC-based or some other Tool that comes with Spring. But I don't know how to do it.
My Spring configuration looks like that:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Get database driver XA properties from file -->
<util:properties id="jdbcConfiguration1" location="classpath:jdbcconfiguration1.properties"/>
<util:properties id="jdbcConfiguration2" location="classpath:jdbcconfiguration2.properties"/>
<bean id="dataSourceA"
class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName"><value>XADBMS01</value></property>
<property name="xaDataSourceClassName"><value>org.postgresql.xa.PGXADataSource</value></property>
<property name="xaProperties" ref="jdbcConfiguration1"/>
<property name="poolSize"><value>1</value></property>
</bean>
<bean id="dataSourceB"
class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
...
<property name="poolSize"><value>1</value></property>
</bean>
<!-- Construct Atomikos UserTransactionManager, needed to configure Spring -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<!-- when close is called, should we force transactions to terminate or not? -->
<property name="forceShutdown">
<value>true</value>
</property>
</bean>
<!-- Also use Atomikos UserTransactionImp, needed to configure Spring -->
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout"><value>300</value></property>
</bean>
<!-- Configure the Spring framework to use JTA transactions from Atomikos -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager"><ref bean="atomikosTransactionManager" /></property>
<property name="userTransaction"><ref bean="atomikosUserTransaction" /></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
......
</beans>
Is it required to use Hibernate? I do not want to use hibernate, because i think it is do complicated for my needs. Is it possible to do that "Spring-based"?
So, you have configured two DataSources and want to know how to use in a way consistent with JtaTransactionManager you declared.
Spring provides two options:
(Recommended) Use JdbcTemplate. Operations on JdbcTemplate automatically participate in the current transaction.
Use DataSourceUitls.getConnection() to obtain a Connection that's aware of the current transaction, and execute arbitrary JDBC operations on it.
In both cases you need to define transaction boundaries in your code using #Transactional or TransactionTemplate, as described in 11. Transaction Management.
Related
I am using a JNDI datasource that is configured in tomcat server. I want to avoid storing the password as clear text and also i have an existing encryption logic available in the application used which i want to use to encrypt the database password.
<Resource name="jdbc/testdb" auth="Container"
factory="com.zaxxer.hikari.HikariJNDIFactory"
type="javax.sql.DataSource"
minimumIdle="5"
maximumPoolSize="50"
connectionTimeout="300000"
driverClassName="org.mariadb.jdbc.Driver"
jdbcUrl="jdbc:mysql://localhost:3307/testdb"
dataSource.implicitCachingEnabled="true"
connectionTestQuery="Select 1" />
Considering this use case and the possible solutions available online i decided to use org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter for providing the username and password for the database using the code
<bean id="dataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/testdb" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter">
<property name="targetDataSource" ref="dataSource1"/>
<property name="username" value="${dataSource.username}"/>
<property name="password" value="#{passwordDecryptor.decryptedString}"/>
</bean>
This approach works for me for making connections to MSSQL database but quite strangely fails on MariaDB with the error "Access denied for user ''#'localhost' (Using Password :NO)". I wonder if this issue has got anything to do with the HikariCP connection pool, as the same works with C3P0 implementation without any issues.
Also I would like to know if this is the right approach and please suggest if this can improved for getting better performance.
Ok, I've taking a peek around, give this a shot:
<Resource name="jdbc/testdb" auth="Container"
factory="org.apache.naming.factory.BeanFactory"
type="com.zaxxer.hikari.HikariDataSource"
minimumIdle="5"
maximumPoolSize="50"
connectionTimeout="300000"
driverClassName="org.mariadb.jdbc.Driver"
jdbcUrl="jdbc:mysql://localhost:3307/testdb"
connectionTestQuery="Select 1" />
What is different? We're not using the com.zaxxer.hikari.HikariJNDIFactory. Why? Because the HikariJNDIFactory uses the HikariDataSource(HikariConfig config) constructor, which immediately instantiates the underlying datasource, after which the configuration can no longer be changed.
Using the BeanFactory, we call the default constructor, which does not instantiate the underlying datasource until the first call to getConnection(), which means we are free to set the username/password after the JNDI lookup.
On the Spring side:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource"
jndi-name="jdbc/testdb"
cache="true"
lookup-on-startup="true"
expose-access-context="true">
<property name="dataSourceProperties">
<props>
<prop key="user">${dataSource.username}</prop>
<prop key="password">${passwordDecryptor.decryptedString}</prop>
</props>
</property>
</jee:jndi-lookup>
I believe this should work, or something very close to it. I am going to make an addition to HikariCP too honor the passing of the JNDI environment so that <jee:environment> can be used inside of the <jee:jndi-lookup> tag for a cleaner solution.
I have a Spring 3.2 application that uses Hibernate 4 and Spring Transactions. All the methods were working great and I could access correctly the database to save or retrieve entities.
Then, I introduced some multithreading, and since each thread was accessing to db I was getting the following error from Hibernate:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
I read from the web that I've to add <prop key="hibernate.current_session_context_class">thread</prop> to my Hibernate configuration, but now every time I try to access the db I get:
org.hibernate.HibernateException: saveOrUpdate is not valid without active transaction
However my service methods are annotated with #Transactional, and all was working fine before the add of <prop key="hibernate.current_session_context_class">thread</prop>.
Why there is no transaction although the methods are annotated with #Transactional? How can I solve this problem?
Here is my Hibernate configuration (including the session context property):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- Hibernate session factory -->
<bean
id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
<property name="dataSource" >
<ref bean="dataSource" />
</property>
<property name="hibernateProperties" >
<props>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.dialect" >org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
</props>
</property>
<property name="annotatedClasses" >
<list>
...
</list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
When using spring and spring managed transactions never mess around with the hibernate.current_session_context_class property UNLESS you are using JTA.
Spring will by default set its own CurrentSessionContext implementation (the SpringSessionContext), however if you set it yourself this will not be the case. Basically breaking proper transaction integration.
The only reason for changing this setting is whenever you want to use JTA managed transactions, then you have to setup this to properly integrate with JTA.
I am first time making a webapp for Jboss server.
For JBoss we have the jndi details, but I am wondering how to create the Datasource using it in spring application context.
If anyone has an example to create the connection, please share it.
I will put this sample here.
just to show another way to do it.
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:jboss/datasources/DSName</value>
</property>
</bean>
I found the solution
Add below configuration to applicationContext.xml
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"
<jee:jndi-lookup expected-type="javax.sql.DataSource" id="dataSource" jndi-name="java:jboss/SAMPLE_JNDI"/>
I have defined DataSource in Context.xml(JNDI) and i would like to use with JPA transaction manager in Spring application Context.xml. I don't want to use JTA Transaction since i am using tomcat server.
Could anyone help me how can i achieve this with an example? I am using #transactional annotations in DAO's and services.
Regards
Vijay
you can Define DataSource: Use JndiObjectFactoryBean class. To define the data source by providing JNDI binding name.
<!– Data Source JNDI –>
<bean id=”dataSource” class=”org.springframework.jndi.JndiObjectFactoryBean”>
<property name=”jndiName”>
<value>jdbc/SampleDS</value>
</property>
</bean>
Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- Provides access to the JNDI datasource -->
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
<!-- Defines transaction manager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Enables transactional behavior -->
<tx:annotation-driven />
<!-- other <bean/> definitions here -->
</beans>
Use <jee:jndi-lookup/> to get access to the JNDI data source. Then, pass the obtained datasource reference to an appropriate PlatformTransactionManager such as DataSourceTransactionManager.
Also, <tx:annotation-driven /> should be provided for the #Transactional annotations to be activated.
To obtain a good understanding of Spring transaction management, I would recommend reading chapter 10 of Spring reference available here.
Environment
There is an existing software with propietary way of clustering, which should be moved to use JMS
The customer do not want to pay for setting up and mainaining a
messaging system, so it can be used ONLY if I can embedd the whole
messaging into the existing virtual machines
Broker instances and consumers should be in the same JVM. Consumers
should be able to connect to remote broker in failover situations, since all consumers no matter in which JVM will they run, should have ONE input queue.
it would be nice if the consumers would use direct method calls to
communicate with local broker
Demo Project
I have created a really simple demo (eclipse) project with ActiveMQ + Maven + Spring (the whole project is at http://www.woofiles.com/dl-279452-fOcsWkcm-activemq.zip). If you try it, change the dataDirectory of activemq, since it is a wired absolute path till now.
I try to start up a broker from spring, and also a set of consumers. See Spring config below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_FALLBACK" />
</bean>
<bean id="embeddedBroker" class="org.apache.activemq.broker.BrokerService"
destroy-method="stop" init-method="start" >
<property name="brokerName" value="conversion" />
<property name="dataDirectory"
value="c:\eclipseWithMaven\activemq\working\conversion" />
<property name="schedulerSupport" value="false" />
<property name="transportConnectorURIs">
<list>
<value>tcp://127.0.0.1:600${idOfClusterNode}</value>
</list>
</property>
<property name="managementContext">
<bean class="org.apache.activemq.broker.jmx.ManagementContext">
<property name="connectorPort" value="201${idOfClusterNode}" />
</bean>
</property>
</bean>
<!-- depends-on see why at http://activemq.apache.org/vm-transport-reference.html -->
<!--depends-on="embeddedBroker" -->
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="embeddedBroker">
<property name="brokerURL">
<value>failover:(vm:/conversion,tcp://127.0.0.1:6001,tcp://127.0.0.1:6002)</value>
</property>
</bean>
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsFactory" p:sessionCacheSize="10" />
<bean id="container"
class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="concurrentConsumers" value="10" />
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="messageListener" ref="conversion" />
<property name="destination" ref="conversionInputQueue" />
</bean>
<bean id="conversionInputQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="conversionInputQueue" />
</bean>
<bean id="conversion" class="activemq.Conversion"
p:clusterId="${idOfClusterNode}" />
</beans>
I simply try to start up one or two instances of the activemq.ConversionDemo class with different parameters used by spring & log4j config. The environment entries of the run config looks like this:
Instance 1 : -DidOfClusterNode=1 -DidOfOtherClusterNode=2 -DlogFile=conversion1.log
Instance 2: -DidOfClusterNode=2 -DidOfOtherClusterNode=1
-DlogFile=conversion2.log
If I start up one instance, it is fine. If two is running the following problems occoured:
The second broker will not start up at all. It says it does not have the lock. Its fine, but I supposed, it just starts up a thread asynchronously, and will give back the control to spring. But it seems, that it wont let spring to continue.
SimpleMessageListenerContainer also seems like holding the control
over spring till all consumers are started.
What I want
I want to meet the requirements above
I think I have to start up both brokers and consumers asynchronously,
which I cannot really do in spring with this config
It would be nice to have real load balancing between brokers. It seems, that ActiveMQ is prepared only for failover.
If my needs cannot be satisfied by ActiveMQ, please recommend
other free solution.
If you need further info, just let me know.
EDIT
I think ActiveMQ supports my needs, I just need to understand "network of brokers". So I guess I have to have two filestore, and a network from my two brokers.
if you point 2 brokers at the same file store, then the first one will get the lock and the 2nd will block until the lock is released...this is a shared file system master/slave setup
if you want an active/active setup, then you'll need to use separate file stores and wire them together as a network of brokers