Getting database connection in Spring Batch service classes - spring-batch

I have a few cases where I want to get a connection to a configured database in a Spring Batch service class, for example in a processor or a custom writer. I can inject the data source into my class but I want to get a connection to it. The out of the box Spring Batch database readers and writers automagically get a connection so I want to use that rather than write my own connection code. So in the writer example say my class uses ItemWriterAdapter and in it I need to do a query. How can I grab an established connection to a configured data source?

Add this to your context.xml (assuming dataSource bean is defined)
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
In your Writer or Processor, inject this jdbcTemplate
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
Now you may query with Spring Jdbc using the same datasource http://static.springsource.org/spring/docs/current/spring-framework-reference/html/jdbc.html

Related

MongoTemplate - get the connection URI

We are injecting the mongotemplate using the spring config
<mongo:mongo-client id="mongoClient" connection-string="${mongodb.connect-string}">
<mongo:client-settings read-preference="PRIMARY_PREFERRED" retry-writes="true" />
</mongo:mongo-client>
<mongo:db-factory id="mongoDbFactory" dbname="${mongodb.database}" mongo-client-ref="mongoClient" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
#Inject
private MongoTemplate mongoTemplate;
Now we are creating a separate integration checkpoint to ensure the mongo db is available and show a green or red traffic light. Is it possible to get the connection URI from the instantiated mongotemplate or I should use ${mongodb.connect-string} to build the integration point?
In the older version of mongotemplate (springDataMongoDb : 1.8.2) we were able to use the following method to get the address
ServerAddress address = mongoTemplate.getDb().getMongo().getAddress();
but with spring 3.4.6 it is not possible to get the address using the above.
I would suggest using ${mongodb.connect-string} as you suggested since this is much more straight forward and doesn't require you to have to wire in the mongo template just to return a constant value when that is the exact propose of spring configs via the #Value annotation.
But a better solution to the green/red for mongo connectivity is using spring actuator which already has this functionality built in.
https://reflectoring.io/spring-boot-health-check/

Spring Boot with application managed persistence context

I am trying to migrate an application from EJB3 + JTA + JPA (EclipseLink). Currently, this application makes use of application managed persistent context due to an unknown number of databases on design time.
The application managed persistent context allows us to control how to create EntityManager (e.g. supply different datasources JNDI to create proper EntityManager for specific DB on runtime).
E.g.
Map properties = new HashMap();
properties.put(PersistenceUnitProperties.TRANSACTION_TYPE, "JTA");
//the datasource JNDI is by configuration and without prior knowledge about the number of databases
//currently, DB JNDI are stored in a externalized file
//the datasource is setup by operation team
properties.put(PersistenceUnitProperties.JTA_DATASOURCE, "datasource-jndi");
properties.put(PersistenceUnitProperties.CACHE_SHARED_DEFAULT, "false");
properties.put(PersistenceUnitProperties.SESSION_NAME, "xxx");
//create the proper EntityManager for connect to database decided on runtime
EntityManager em = Persistence.createEntityManagerFactory("PU1", properties).createEntityManager();
//query or update DB
em.persist(entity);
em.createQuery(...).executeUpdate();
When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback.
Now, I am trying to migrate this application to Spring Boot.
The problem I encounter is that there is no transaction started even after I annotate the method with #Transactional(propagation = Propagation.REQUIRED).
The Spring application is packed as an executable JAR file and run with embadded Tomcat.
When I try to execute those update APIs, e.g. EntityManager.persist(..), EclipseLink always complains about:
javax.persistence.TransactionRequiredException: 'No transaction is currently active'
Sample code below:
//for data persistence
#Service
class DynamicServiceImpl implements DynamicService {
//attempt to start a transaction
#Transactional(propagation = Propagation.REQUIRED)
public void saveData(DbJndi, EntityA){
//this return false that no transaction started
TransactionSynchronizationManager.isActualTransactionActive();
//create an EntityManager based on the input DbJndi to dynamically
//determine which DB to save the data
EntityManager em = createEm(DbJndi);
//save the data
em.persist(EntityA);
}
}
//restful service
#RestController
class RestController{
#Autowired
DynamicService service;
#RequestMapping( value = "/saveRecord", method = RequestMethod.POST)
public #ResponseBody String saveRecord(){
//save data
service.saveData(...)
}
}
//startup application
#SpringBootApplication
class TestApp {
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
}
persistence.xml
-------------------------------------------
&ltpersistence-unit name="PU1" transaction-type="JTA">
&ltproperties>
&lt!-- comment for spring to handle transaction??? -->
&lt!--property name="eclipselink.target-server" value="WebLogic_10"/ -->
&lt/properties>
&lt/persistence-unit>
-------------------------------------------
application.properties (just 3 lines of config)
-------------------------------------------
spring.jta.enabled=true
spring.jta.log-dir=spring-test # Transaction logs directory.
spring.jta.transaction-manager-id=spring-test
-------------------------------------------
My usage pattern does not follow most typical use cases (e.g. with known number of DBs - Spring + JPA + multiple persistence units: Injecting EntityManager).
Can anybody give me advice on how to solve this issue?
Is there anybody who has ever hit this situation that the DBs are not known in design time?
Thank you.
I finally got it work with:
Enable tomcat JNDI and create the datasource JNDI to each DS programmatically
Add transaction stuff
com.atomikos:transactions-eclipselink:3.9.3 (my project uses eclipselink instead of hibernate)
org.springframework.boot:spring-boot-starter-jta-atomikos
org.springframework:spring-tx
You have pretty much answered the question yourself: "When deployed in a EJB container (e.g. WebLogic), with proper TransactionAttribute (e.g. TransactionAttributeType.REQUIRED), the container will take care of the transaction start/end/rollback".
WebLogic is compliant with the Java Enterprise Edition specification which is probably why it worked before, but now you are using Tomcat (in embedded mode) which are NOT.
So you simply cannot do what you are trying to do.
This statement in your persistence.xml file:
<persistence-unit name="PU1" transaction-type="JTA">
requires an Enterprise Server (WebLogic, Glassfish, JBoss etc.)
With Tomcat you can only do this:
<persistence-unit name="PU1" transaction-type="RESOURCE_LOCAL">
And you need to handle transactions by your self:
myEntityManager.getTransaction.begin();
... //Do your transaction stuff
myEntityManager.getTransaction().commit();

Apache Ignite: Failed to unmarshal discovery data

I'm trying to use the Ignite Jdbc connection; with my goal to be able to call the cache from any client over Jdbc.
I've got a number of scenario's working; so have data loaded correctly; and can run sql queries 'directly' against the cache.
When I try to call from a separate client with
ResultSet rs = conn.createStatement().executeQuery("select * from my_table");
I hit an error:
class org.apache.ignite.IgniteCheckedException: Failed to find class with given class loader for unmarshalling (make sure same versions of all classes are available on all nodes or enable peer-class-loading) [clsLdr=URLClassLoader with NativeCopyLoader with RawResources(
Is there a way to prevent the Ignite jdbc connection from trying to do any unmarshalling?
I would like my client to be as agnostic as possible to the Ignite classes. For example; I would like to swap out calling mariaDb to Ignite - with as little code change as possible on the client side.
If I'm thinking about things the wrong way; then answer along the lines of No, that will never work because ... are more than welcome too.
Thanks
Brent
If you don't want copy libs to client's lib folder, you can turn on Peer Class Loading:
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
...
<!-- Explicitly enable peer class loading. -->
<property name="peerClassLoadingEnabled" value="true"/>
...
</bean>
Also, you can work with BinaryObject, instead of your classes. Here is some example of using sql with BinaryObjects. More information on the binary format is provided here.

How to configure Bean class in root-context.xml file using STS in Spring?

I am new to Spring and going to develop a spring MVC Application.What is the best way to write root-context.xml for Bean class properties?
For database Connection I want to use Spring jdbc(JdbcTemplate).Can you please suggest me the best way to do so?
You're going to need to set up a DataSource and then create a JdbcTemplate bean that utilizes it. The Spring JDBC Reference Documentation provides examples and really good explanations on how to accomplish this.
Here's a basic bean definition for a datasource. The properties specified will depend on the database that you are using.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
Then you can create a jdbcTemplate bean that uses the dataSource or you can instantiate the jdbcTemplate within your code.
The JdbcTemplate API mentions this:
Can be used within a service implementation via direct instantiation with a DataSource reference, or get prepared in an application context and given to services as bean reference. Note: The DataSource should always be configured as a bean in the application context, in the first case given to the service directly, in the second case to the prepared template.

TopLink with JPA taking more connections

In my application we are using Toplink with Jpa.
Here the problem is we are using stored procedures in this application, we are taking the connection using Jndi connection for Stored Procedure calling, and we are using EntityManger for remaining queries. But here if we launching the application it is taking two connections from connection pool. After the application launching I am calling the Stored Procedure(sp)
one sp I am taking one connection but in websphere connection pool it is creating two connections?
can U plese help me how to overcome this problem.....
I won't using JTA, to get the JDBC connection I am using
EntityManager em = getJpaTemplate().getEntityManagerFactory().createEntityManager();
this way I am getting the JDBC Connection...and I configured the persitence.xml file following code...
<properties>
<property name="toplink.logging.level" value="OFF"/>
<property name="toplink.cache.type.default" value="NONE"/>
<property name="com.thoughtinc.runtime.persistence.sql.syntax" value="db2" />
</properties>
So, please kindly look into this and tell me any if I am doing any wrong here.
Are you using JTA or non-JTA? Are you releasing the connection back to the pool after using it?
Depending on your configuration (include your persistence.xml), if you have a non-JTA login configured TopLink may use this for non-transactional read queries. This is configurable in your persistence.xml.
To get the JDBC Connection from a TopLink (EclipseLink) EntityManager use em.unwrap(Connection.class)