MyBatis - defining a global parameter - java

First the problem: I'm using XML-defined queries and the SQL contains database name as part of a table name. For example: SELECT * from mydb.bar. Unfortunately, databases are created/named all over the place and mudb part is really dynamic and can change at any moment. So I wanted to replace it with a property so it would look like SELECT * FROM ${dbname}.bar and then I defined the following section in mybatis-config.xml:
<properties>
<property name="dbname" value="mydb"/>
</properties>
But when I run the query ${dbname} evaluates to null. Same happens if I define this property in the properties file. I would hate to pass this as part of the each call parameters since this is truly a global property. Can this be done? And if yes - how?

Yes, you can! This is kind of a weird undocumented feature maybe. When building your Configuration object, do something like this. (org.apache.ibatis.session.Configuration)
configuration.getVariables().put("global_param", "123");
Then in your XML map, you can reference.
select * from ${global_param}

I had the same issue using Spring+MyBatis, and solved it by setting 'configurationProperties' using my Spring XML definition for sqlSessionFactory. My example below shows how to set a custom global property named 'encryptionKey', with a value which you can either hard-code in the XML file, or load from an external file using the context:property-placeholder tag (as below).
<context:property-placeholder location="/WEB-INF/spring/config-datasource.properties" />
<beans:bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="typeAliasesPackage" value="com.example.model" />
<beans:property name="configurationProperties">
<beans:props>
<beans:prop key="encryptionKey">${jdbc.encryptionKey}</beans:prop>
</beans:props>
</beans:property>
</beans:bean>

I was using an XML configuration but not Spring and set a property inside the Configuration object but discovered that had to be done before the mapper files are loaded (see here). I abandoned the Configuration object approach and went with this approach, which worked for me:
Reader reader = Resources.getResourceAsReader("..../mybatis-config.xml");
Properties properties = new Properties();
properties.setProperty("dbname", "mydb");
SqlSessionFactory.factory = new SqlSessionFactoryBuilder().build(reader, "development", properties);
Then, as Andy Pryor posted, use select * from ${dbname} in the XML mapper.

Related

Spring 3.5 Setting an xml property, handling the default value using PropertyPlaceholderConfigurer

I'm working with an old project using Spring 3.5 and xml config.
In the application context I am setting a property to an externally configured xml file value so something like this -
<bean id="myService" class="com.mypath.MyService">
<property name="myProperty" value="${myValue}:myDefaultValue" />
</bean>
This all works properly.
In some tests though there is a test-context.xml coming into play which sets the values of the properties like this
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<util:properties>
<prop key="myProperty">myValue</prop>
</util:properties>
</property>
</bean>
This is failing to 'understand' the default value. If I leave the value blank I get :myDefaultValue returned. If I set myValue like my example shows I get myValue:myDefaultValue.
I had a quick peak at the PropertyPlaceholderConfigurer and it looks very basic without many options. I've not used it before. Does anyone know if there is a simple way for me to handle the defaults ? Or maybe I have to use a different method to set the test context values ?
You have your syntax slightly off. The default value for the expression goes inside the braces, like so: ${myValue:defaultValue}.

Spring Batch - more than one writer based on field value

I am working on spring batch, for writer currently using FlatFileItemWriter.
I would like to write my input file content to more than one flat file based on some field value. Is Spring batch support any kind of functionality by default.[something similar to CompositeItemWriter]
For example, my input file content is something like this.
john,35,retail,10000
joe,34,homeloan,20000
Amy,23,retail,2000
Now i would like to write two different files based on third column, it means row 1 and row 3 should go to file1 and row 2 should go to file2.
My writer configuration is:
<bean id="fileWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:C:/output.dat"/>
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|" />
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="field1,field2...." />
</bean>
</property>
</bean>
</property>
</bean>
Take a look at the ClassifierCompositeItemWriter. This ItemWriter implementation allows you to define a Classifier that chooses which of the defined delegate ItemWriter instances to delegate to. In your case, you'd create a Classifier that decided based on field4 and delegate the writing to the appropriate instance of the FlatFileItemWriter.
You can read more about the ClassifierCompositeItemWriter in the documentation here: http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/item/support/ClassifierCompositeItemWriter.html
Use a ClassifierCompositeItemWriter
Calls one of a collection of ItemWriters for each item, based on a
router pattern implemented through the provided Classifier.
Router pattern is based on bean content

How to use property file in util:properties?

I must use spring util:properties, however, I need something like this:
if propertyFile x exists, use x, otherwise, use y.
Can you please advise how I can obtain this?
May be this is half solution! Spring can load the wildcard resources. Please see spring <util:properties /> with wildcards
In this way you can name your files like: x-config.properties and y-config.properties:
<bean id = "config"
class = "org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name = "locations"
value = "classpath*:somefolder/*-config.properties" />
</bean>
If both x and y exist both of them are loaded.
Actually, there is an option ignoreResourceNotFound, but it isn't available for namespace component. You have to use PropertiesFactoryBean directly:
<bean id="props" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations" value="y.properties, x.properties"/>
<property name="ignoreResourceNotFound" value="true"/>
</bean>
If your x.properties doesn't exist, it will be ignored and the properties from y.properties will remain.
If x.properties exists and it contains the same keys as y.properties, they override those from y.properties, because all locations are loaded one by one respectively.

Spring - PropertyOverrideConfigurer multiple configuration lines for ONE bean property

I have a spring xml file with some beans, two of which I have pasted below:
<bean
class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations">
<value>classpath:projectname-override.properties</value>
</property>
</bean>
<bean id="myBeanName"
class="my.company.department.svc.spring.WmSvcJndiDataSource">
<property name="jndiName">
<value>jdbc/XXXXX</value>
</property>
<property name="jndiTemplate">
<ref bean="myJndiTemplate" />
</property>
</bean>
The properties file referred to in the first bean is very small (currently only one line), and looks like this:
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV3
It is my understanding that PropertyOverrideConfigurer in the first bean
pushes values from a properties file into bean definitions
So the value of the jndiName property in the myBeanName bean will turn from
jdbc/XXXXX
to
java:comp/env/jdbc/XXXXX_YYY_DEV3.
This works and it partially does what we want. I need to expand the functionality so that I can handle multiple deployment environments. This currently replaces the bean property for one of our development environments (dev3). I think I need spring to dynamically alter "myBeanName" for about 25 different dev environments.
For example, I think I want my properties file to look like this
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV1
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV2
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV3
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV4
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV5
...
...
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV24
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV25
I have no idea if this is the right approach.
Any ideas?
Thanks in advance.
NOTE
my.company.department.svc.spring.WmSvcJndiDataSource in the second bean is simply a class we extended off of org.springframework.jndi.JndiObjectFactoryBean, FYI. I don't know if that is important to know or not.

Concatenate strings within a Spring XML configuration file?

I have a String value in a Spring configuration file that comes to be as the result of a JNDI lookup -- it happens to be a path name:
<jee:jndi-lookup id="myAppHomeDir" jndi-name="myAppHomeDir" />
Now I need to concatenate on to the end of this path another string and hand it off to another Spring bean as follows (which of course doesn't work):
<bean id="LogPath" class="org.mystuff.initBean">
<property name="logDirectory">
<jee:jndi-lookup id="myAppHomeDir"
jndi-name="myAppHomeDir" /> + "/logs"
</property>
</bean>
Is there a simple way to do this without me having to write a utility class in Java?
Try using Spring EL (expression language). I would try the following (not tested):
<jee:jndi-lookup id="myAppHomeDir" jndi-name="myAppHomeDir" />
<bean id="LogPath" class="org.mystuff.initBean">
<property name="logDirectory" value="#{myAppHomeDir+'/logs'}"/>
</bean>
Not quite sure if it would work. The thing that troubles me is the cast from File (I guess) to String when concatenating. So if the previous one didn't work, I would try:
#{myAppHomeDir.canonicalPath+'/logs'}
Let us know if it works.

Categories

Resources