Spring Integration jdbc stored procedure custom rowmapper - spring-integration

I am newbie to Spring Integration and am using Spring 4.2.4.
I am trying to invoke stored procedure with jdbc:stored-proc-outbound-gateway. i am already using spring jdbc.
Stored procedure is returning cursor and am using customRowMapper like below
new SqlOutParameter(A_RC, OracleTypes.CURSOR, null, new MyCustomDataExtractor())
MyCustomDataExtractor implements SqlReturnType and it returns custom object.
Now the question is how can I achieve this in SI jdbc stored proc.piece of my code here..
...
<int-jdbc:sql-parameter-definition name="A_RC" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
...
<int-jdbc:returning-resultset name="A_RC" row-mapper="a.b.c.MyCustomDataExtractor"/>
...
Spring expect this as a row mapper. should I use any transformer here? Please advice.
Note : I have to return multiple resultset.

Actually with the CURSOR type you are good to go with just a returning-resultset and RowMapper implementation.
With that you don't need to worry about any SqlReturnType and just map the row to your domain object directly.
I'm even sure you can rework your MyCustomDataExtractor to the RowMapper contract.
Note: with returning-resultset defintion you don't need to
specify the sql-parameter-definition for the same OUT param. The component identifies them correctly as a OutParameter.
And, yes you can have several returning-resultset for CURSOR parameters.

I added the return-type in the sql-parameter-definition and removed returning-resultset.
<int-jdbc:sql-parameter-definition name="A_RC" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT" return-type="ed"/>
and here "ed" is nothing but the bean reference for a.b.c.MyCustomDataExtractor.
<bean id="ed" class="a.b.c.MyCustomDataExtractor"/>

Related

how to write class to do Database operation - spring integeration

i have a code
<int-jpa:updating-outbound-gateway
request-channel="nativeQlChannel" auto-startup="true"
native-query="update Transactions t set t.transaction_Status = :transactionStatus where t.bank_Reference_Number = :bankReferenceNumber "
entity-manager="entityManager" persist-mode="PERSIST" reply-channel="nativeQlChannelOne"
use-payload-as-parameter-source="false">
it works fine , but as i need to execute insert operation on more than one table , and i am not able to do that by this configuration ,
how can i write code using spring integration JPA class like JpaOutboundGatewayFactoryBean , or any other so that i can perform DB operation in my java code.
First of all it isn't JPA responsibility to worry about "more than one table".
It operates only with the entities as a high level abstraction.
Although yes, you can map your entity to several tables. Plus there is cascade insert when you have dependencies.
In addition that component supports native-query/native-query if you need more graceful control for your DB operation.
As for Java configuration correct: you should use JpaOutboundGatewayFactoryBean #Bean together with the #ServiceActivator to reach similar behavior.
You can find more samples in the Reference Manual.
i am able to fix it by using
ExpressionEvaluatingParameterSourceFactory
ExpressionEvaluatingParameterSourceFactory paramFactory=new ExpressionEvaluatingParameterSourceFactory() ;
paramFactory.setParameters(paramList);
but i got exception
WARN - o.s.i.e.ExpressionUtils: Creating EvaluationContext with no beanFactory
java.lang.RuntimeException: No beanfactory
although it is not stopping any functionality . so i just ignored it.

How to set payload as constructor-arg value in service-activator

I've started with SI and kind of stuck right now as we want to use SI in one of our existing project avoiding changes where we can.
A bean which we would be using as a service activator accepts an constructor argument of a java object.
that object is in the payload but then I'm unable to set it using inner bean usage of service-activator
<service-activator input-channel="ADMIN_TEST_CONNECTION" method="testConnection">
<beans:bean class="mypackage.request.AdminRequestProcessor">
<beans:constructor-arg value="payload"/>
</beans:bean>
</service-activator>
it's complaining about Could not convert argument value of type [java.lang.String] to required type.
Please help in how to access payload and set it as an constructor argument.
If I go via non- constructor arg route and change existing java object then it works with this call in the service activator
expression="#bean.testConnection(payload)"/>
but I don't wish you to change the existing java code until there is no other way.
I think you don't have choice unless change something or add code around existing.
Service-Activator performs its functionality against each incoming message in the input-channel. And that functionality is exactly method invocation where Message is used as a context for method arguments.
Not sure what you are going to do with that payload, but that doesn't look correct to use statefull object like your AdminRequestProcessor. Just don't forget that you may have many incoming message, but service-activator should be consistent.
Plus don't forget that <beans:bean> is singleton, so your AdminRequestProcessor is instantiated only once.
Looking to your sample I'd suggest something like this:
expression="new AdminRequestProcessor(payload).testConnection()"/>
If you really don't want to change anything in your code.
Everything rest you can find in the Reference Manual.

Design: Spring Integration jdbc best practice

After using Spring Integration in a project, my observation is to use jdbc adapter or gateway only at the start or end of the flow. If we use them in the middle of flow then it will become too verbose and complex.
For example:
<jdbc:outbound-gateway
query="select * from foo where
c1=:headers[c1] AND
c2=:headers[c2] AND
c3=:headers[c3] AND
c4=:headers[c4]"
row-mapper="fooMapper" data-source="myDataSource" max-rows-per-poll="100000" />
<int:service-activator ref="serviceActivator" method="processFoo" />
In the above <jdbc:outbound-gateway>, we need to pass all the placeholders (c1, c2, c3, c4) in the header of Message. We need to look back and forth in java code and xml file for any change in where condition or when there are too many where clauses.
It is also error prone. For example, if we misspelled :headers[c1] to :headers[d1] then it will not throw any exception and replace :headers[d1] with null.
If query does not return any row then it will throw exception by default. So, we have to use requires-reply="false" to change default behaviour.
If we want to proceed when query does not return any value then we have to add advice to gateway, as shown below:
<jdbc:outbound-gateway ... >
<jdbc:request-handler-advice-chain>
<bean class="com.service.NullReplyAdvice" />
</jdbc:request-handler-advice-chain>
</jdbc:outbound-gateway>
Please correct me if there are flaws in understanding of the concept.
We need to look back and forth in java code and xml file for any change in where condition or when there are too many where clauses.
It's true even for raw Java code around the JDBC: if you change the model you, of course, should change the SELECT, because it is just a String. And that's why there is a lot of work to make it type-safe - ORM, QueryDSL, Spring-Data etc.
if we misspelled :headers[c1] to :headers[d1] then it will not throw any exception and replace :headers[d1] with null.
That's because the headers is just a Map and it's truth that you get null, if there is no such a key in the map. To overcome that typo issue you can use POJO payload with getters, or some custom header, and again - the POJO with getters. In this case you end up with exception that there is no such a property against object. Although you'll see that issue only at runtime, not on compile. And again the same is with Hashtable - only at runtime.
So, we have to use requires-reply="false" to change default behaviour.
You should understand it at design time: allow or not to return nothing for the component.
The last idea is good. Wouldn't you mind to share your NullReplyAdvice?
Actually I achieve the same with <filter> before the JDBC gateway: to determine if there is something to fetch by count(*) query. From there I can lead my flow to the different logic, rather than the direct flow, when SELECT returns rows.
UPDATE
When you want to use Model object to keep business-specific values within Message, it's just enough to put this object to the header:
public class Foo {
private String foo1;
private String foo2;
public String getFoo1() {
return foo1;
}
public String getFoo2() {
return foo2;
}
}
...
MessageBuilder.withPayload(payload).setHeader("foo", foo).build();
...
<jdbc:outbound-gateway
query="select * from foo where
c1=:headers[foo].foo1 AND
c1=:headers[foo].foo2"/>

Bean Autowiring problem

I am starter in mutithreading. I am trying to index my data into solr.For that I was writing the following code
I am getting null pointer exception in the line highlighted
You need to add the following:
<context:annotation-config/>
You need to set the path for autowiring package scan and in your case it will be:
<context:component-scan base-package="a.b.c" />
After it you need to mark the class as candidate for autowiring:
#Component("indexTask")
#Scope("prototype")
IndexTask implements Callable<IndexObject>
{
//ommited
}
Next you can remove indexTask bean configuration from xml file. your package will be created automatically.
Hope it helps.
Autowiring doesn't happen automatically, you need to configure it. See the Spring docs for detail, but essentially you need to add
<context:annotation-config/>

Spring transactions - Mixing #Transactional with <tx:advice> into a custom annotation

My goal is to have some way of declaring my service classes as transactional. I dont want to leave it as an explicit declaration in spring configuration. Many times in past we have created new services and forgot to declare transactions around them. Hence my intention is that if i have something like #TransactionalService custom annotation, it should do the following :-
1. provides transactional support
2. declares some default rules for transactional support as spring currently provides as shown below. But unlike spring, i would like the below to be part of my #TransactionService annotation.
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
Any advice would be valuable?
Sure, instead of creating a new annotation, you could just put your transactionnal services in the same package, and then your pointcut (only one for all you transactionnal services) will look like this :
<aop:config>
<aop:pointcut id="transactionnalServiceMethods" expression="execution(* x.y.transactionnalservice.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionnalServiceMethods"/>
</aop:config>
The advice is the same as above :
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

Resources