More property setting woes ... even with MapperFactory and PropertyPlaceholderConfigurer

397 views
Skip to first unread message

Rick R

unread,
May 14, 2012, 10:51:57 PM5/14/12
to mybati...@googlegroups.com
Wasted way too many hours on this today since I was looking everywhere except for the Spring MyBatis setup as the culprit. I went away from using a Java config for my MyBatis stuff since there were issues with the whole issues of properties getting loaded and dealing with the Mapper scanner. 

Now, apparently even defining the following messes with the whole setting of properties using the PropertyPlaceholderConfigurer...

If the following is defined:

        <bean id="sqlSessionFactoryForBatch" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="oracleDS"/>
<property name="typeAliasesPackage" value="com.ncs.domain"/>
<property name="typeHandlersPackage" value="com.ncs.typehandler"/>
</bean>

<bean id="batchMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.ncs.batchmapper.BatchMapper"/>
<property name="sqlSessionTemplate">
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactoryForBatch"/>
<constructor-arg index="1" value="BATCH"/>
</bean>
</property>
</bean>

 I will NOT get my properties set for my data source using the ProeprtyPlaceholderConfigurer. (

       <context:property-placeholder location="classpath:test-application.properties"/>
 
       <!--- NONE OF THESE WILL BE SET ! --->
<bean id="oracleDS" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url"  value="${oraUrl}"/>
<property name="username" value="${username}"/>
.....
</bean>

According to the docs I realize these issues happened with the MapperScanner http://www.mybatis.org/spring/mappers.html, but how do I avoid it when just creating a standard MapperFactoryBean? MapperFactory doesn't take an instance of sqlSessionFactoryBeanName like the MapperScanner does?

Thanks!

--
Rick R

Rick R

unread,
May 15, 2012, 12:39:44 AM5/15/12
to mybati...@googlegroups.com
Note the same issue arises if you try to define another MapperScanner and you still end up instantiating a SqlSessionTemplate with a reference to sqlSessionFactory...

<!-- standard mappers -->
       <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ncs.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean> 

<!-- try to map a different package with batch mappers -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ncs.batchmapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryForBatch"/>
</bean>

<bean id="sqlSessionFactoryForBatch" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="oracleDS"/>
<property name="typeAliasesPackage" value="com.ncs.domain"/>
<property name="typeHandlersPackage" value="com.ncs.typehandler"/>
</bean>

   <!-- This coupled with the mapper above causes the problem --> 
<bean id="batchSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactoryForBatch"/>
<constructor-arg index="1" value="BATCH"/>
</bean>



--
Rick R

Eduardo Macarron

unread,
May 15, 2012, 11:07:01 AM5/15/12
to mybati...@googlegroups.com
Hi Rick. I am sorry I have not much time to get deeply into this
issues so sorry if I overlook something important.

The scanner is a bean that runs early during the spring startup
process. It runs before the propertiesplaceholderconfigurer so there
may be a problem with the place holders and you already know. But this
cannot happen with a MapperFactoryBean because it is a normal bean an
will be started once the propertiesplaceholderconfigurer has finished
so the problem must be elsewhere...

2012/5/15 Rick R <ric...@gmail.com>:

Rick R

unread,
May 15, 2012, 12:18:51 PM5/15/12
to mybati...@googlegroups.com
On Tue, May 15, 2012 at 11:07 AM, Eduardo Macarron <eduardo....@gmail.com> wrote:
Hi Rick. I am sorry I have not much time to get deeply into this
issues so sorry if I overlook something important.

The scanner is a bean that runs early during the spring startup
process. It runs before the propertiesplaceholderconfigurer so there
may be a problem with the place holders and you already know. But this
cannot happen with a MapperFactoryBean because it is a normal bean an
will be started once the propertiesplaceholderconfigurer has finished
so the problem must be elsewhere...



There definitely seems to be something odd going on though when you use a combination of 1) multiple SqlSessionFactories and 2) mapper scanner and a MapperFactoryBean definition.
For example, I'm trying to set up a simple example to show you what I'm referring to, and based on this following code with more than one SqlSessionFactoryBean defined... if you include the MapperFactoryBean as part of the deployment, Spring can't decide which SqlSessionFactory to bind to autowire as you can see in this highlighted screen shot: https://img.skitch.com/20120515-s9w2t9eb2p1cu7qj3t46axehk.jpg  HOWEVER, the odd thing is if I remove the MapperFactoryBean definition, then there are no issues auto-wiring.
       
       <!-- THREE SqlSessionFactoryBeans -->
         <bean id="sqlSessionFactoryOne" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSourceOne"/>
<property name="typeAliasesPackage" value="net.learntechnology.empmaint.business.dbone.domain"/>
</bean>

<bean id="sqlSessionFactoryTwo" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSourceTwo"/>
<property name="typeAliasesPackage" value="net.learntechnology.empmaint.business.dbone.domain"/>
</bean>

<bean id="sqlSessionFactoryForBatch" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSourceOne"/>
<property name="typeAliasesPackage" value="net.learntechnology.empmaint.business.dbone.domain"/>
</bean>
        
        <!--- MAPPER SCANNER, this should map sqlSessionFactoryOne to the Mappers scanned.. so why complain about not knowing
         which sqlSessionFacotryBean to autowire when the MapperFactoryBean is included below this definition 
          --->

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="net.learntechnology.empmaint.business.dbone.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryOne"/>
</bean>


        <!--- *********** IF THESE ARE COMMENTED OUT BELOW then no issues with the above -->

<bean id="batchMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="net.learntechnology.empmaint.business.BatchMapper"/>
<property name="sqlSessionTemplate">
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactoryForBatch"/>
<constructor-arg index="1" value="BATCH"/>
</bean>
</property>
</bean>

<bean class="net.learntechnology.empmaint.business.service.BatchService">
<property name="batchMapper" ref="batchMapper"/>
</bean>



--
Rick R

Rick R

unread,
May 15, 2012, 12:42:27 PM5/15/12
to mybati...@googlegroups.com
Ok a bit more info... I removed the MapperScanner completely and now in both apps (the original and the one I was going to provide as an example), I defined *ALL* my mappers as MapperFactoryBeans and then the properties get set just fine (after removing the MapperScannerConfigurer.) Using MapperScannerConfigurer seems to mess with things.. even when I have it defined as the docs point out using sqlSessionFactoryBeanName: 

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ncs.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

I think I'm going to have to just be safe and rely on defining all my Mappers in my xml :(.. sort of stinks since it's now one more ugly piece of xml to maintain, but oh well. Possibly others aren't running into this since they:
1) Aren't defining multiple SqlSessionFactories and/or 2) aren't relying on auto-wiring of things.

I'd be interested in other examples out there of people using the latest spring and spring-mybatis with multiple SqlSessionFactories defined and using the MapperScannerConfigurer and leveraging auto-wiring of mappers.
--
Rick R

Kurth, Felix

unread,
Jan 24, 2013, 8:56:40 AM1/24/13
to mybati...@googlegroups.com

This problem/bug seems to be in the class

SqlSessionDaoSupport

 

It tries to autowire both, a sqlSessionFactory and a sqlSessionTemplate with @Autowired. If the later is found it will be used, regardless whether it belongs itself to the correct SqlSessionFactory. The requested SqlSession points to the wrong database in the end.

 

This will lead to the effect, that you cannot use SqlSessionTemplate and MapperScanner together if multiple databases/SqlSessionFactories are defined.

In my opinion, SqlSessionDaoSupport should not use @Autowired.

 

One can overcome this problem by disabling autowireing for the SqlSessionTemplate:

 

<bean id="batchExecutor" class="org.mybatis.spring.SqlSessionTemplate" autowire-candidate="false">

                        <constructor-arg index="0" ref="sqlSessionFactoryForDatabase1" />

                        <constructor-arg index="1" value="BATCH" />

</bean>

 

In your own service classes, you have to force autowireing with

 

@Resource(name="batchExecutor")

SqlSessionTemplate batchExecutor;

 

Felix

Eduardo Macarron

unread,
Jan 24, 2013, 10:37:49 AM1/24/13
to mybati...@googlegroups.com
Hi Felix,

If there is more than one factory then you cannot use autowire because
you need to specify which one should be injected in your mappers/daos.

Does it fail if you explicitly inject the right template?

2013/1/24 Kurth, Felix <Felix...@globalfoundries.com>:

Kurth, Felix

unread,
Jan 24, 2013, 11:07:56 AM1/24/13
to mybati...@googlegroups.com
Yes i explicitly wire it like this:
 
        <bean id="sqlSessionFactory_1" class="org.mybatis.spring.SqlSessionFactoryBean">
                <property name="dataSource" ref="dataSource_1" />
        </bean>
 
        <bean id="sqlSessionFactory_2" class="org.mybatis.spring.SqlSessionFactoryBean">
                <property name="dataSource" ref="dataSource_2" />
        </bean>
 
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
                <property name="basePackage" value="basepackage1" />
                <property name="sqlSessionFactoryBeanName" value=" sqlSessionFactory_1" />
                <property name="annotationClass" value=" AnnotationClass1" />
                <property name="processPropertyPlaceHolders" value="true"></property>
        </bean>
       
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
                <property name="basePackage" value=" basepackage2" />
                <property name="sqlSessionFactoryBeanName" value=" sqlSessionFactory_2" />
                <property name="annotationClass" value="AnnotationClass2" />
                <property name="processPropertyPlaceHolders" value="true"></property>
        </bean>
 
        <bean id="transactionManager_1"
                class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource_1" />
                <qualifier value="quali_1"/>
        </bean>
 
        <bean id="transactionManager_2"
                class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource_2" />
                <qualifier value="quali_2"/>
        </bean>
 
 
This works all fine up to this point.
 
Now if im including this bean into the ApplicationContext:
 
        <bean id="batchExecutor" class="org.mybatis.spring.SqlSessionTemplate" >
                 <constructor-arg index="0" ref="sqlSessionFactory_1" />
                <constructor-arg index="1" value="BATCH" />
        </bean>
 
All mappers directing to database_2/sglSessionFactory_2 are now going to database_1. The reason is the Dual-Autowireing in SqlSessionDaoSupport.
The MapperScannerConfigurer now prefers the autowired sqlSessionTemplate over the explicitly wired sqlSessionFactory. It should be the other way around?
I even didn’t see the point to use a sqlSessionTemplate template in SqlSessionDaoSupport at all, since it creates a template with the “sqlSessionFactory” anyways?

Eduardo Macarron

unread,
Jan 24, 2013, 12:28:27 PM1/24/13
to mybati...@googlegroups.com
Are you using mybatis-spring 1.1.1?

There is a test with that exact scenario that passes OK.

http://mybatis.googlecode.com/svn/sub-projects/mybatis-spring/trunk/src/test/java/org/mybatis/spring/submitted/autowire/

2013/1/24 Kurth, Felix <Felix...@globalfoundries.com>:

Kurth, Felix

unread,
Jan 24, 2013, 3:10:50 PM1/24/13
to mybati...@googlegroups.com
Yes, it's 1.1.1.
--


Eduardo Macarron

unread,
Jan 24, 2013, 3:49:50 PM1/24/13
to mybati...@googlegroups.com
Felix, in that case, it would be really helpful that you can reproduce
your error using that post so we can have a look at it.

2013/1/24 Kurth, Felix <Felix...@globalfoundries.com>:
> --
>
>

Kurth, Felix

unread,
Jan 25, 2013, 3:23:28 AM1/25/13
to mybati...@googlegroups.com
I've opened a case for the issue:

http://code.google.com/p/mybatis/issues/detail?id=763
--


Eduardo Macarron

unread,
Jan 26, 2013, 2:31:38 AM1/26/13
to mybati...@googlegroups.com
Hello again Felix,

Thanks for filling the issue. I think I managed to provide a fix for
it please give it a try.

Regarding the @Autoriwred annotations, I do also think they do more
harm than good.

We used them to autowire the factory beans generated by the scanner
but I think that can also be achieved by activating the autowire only
for that beans during its creation:

if (!explicitFactoryUsed && !explicitTemplateUsed) {
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}

The problem is that I am afraid that removing @Autowire is a breaking
change and it is too late for that. :(


2013/1/25 Kurth, Felix <Felix...@globalfoundries.com>:

Eduardo Macarron

unread,
Jan 27, 2013, 3:56:32 AM1/27/13
to mybati...@googlegroups.com
Felix, I think we can remove the @Autowire from setSqlSessionTemplate.
I hope that removes some of the know weird side effects.

Just a few people may be affected by the change so I think it is worth.

We are not so sure about removing also the @Autowire from
setSqlSessionFactory but are open to users opinion.

What do you think? Should we remove both for MyBatis-Spring 1.2.0?

Kurth, Felix

unread,
Jan 28, 2013, 5:04:02 AM1/28/13
to mybati...@googlegroups.com
Eduardo,
you're your code change seems to fix the issue. Thank you.

Yes, in my opinion these @Autowire annotations should be avoided. Both, from sqlSessionTemplate and sqlSessionFactory.

I would even go further and throw an exception if the user tries to wire both, a factory and a template since wiring both makes no sense and usually causes problems/undefined state.


BtW. in "SQLSessionTemplate" (trunk) there has been a function added
@Override
public void destroy() throws Exception {
// fix to avoid Spring 3.2 complaining that calling close() fails
}

This seems to work only with Spring 3.2, not with 3.1 as the pom requests.
Please remove "@Override" to fix this.

Cosmetic problem: In the code changes, your refer to the bug number 762, actually it's 763



-----Original Message-----
From: mybati...@googlegroups.com [mailto:mybati...@googlegroups.com] On Behalf Of Eduardo Macarron
Sent: Sonntag, 27. Januar 2013 09:57
To: mybati...@googlegroups.com
Subject: Re: More property setting woes ... even with MapperFactory and PropertyPlaceholderConfigurer

--


Dridi Boukelmoune

unread,
Jan 28, 2013, 5:57:02 AM1/28/13
to mybati...@googlegroups.com
Hi Eduardo,

How hard would a migration be ?

It's probably simpler to break the compatibility and provide a
migration guide. Also most users probably work with a single
datasource so the impact might be minimal.

Best Regards,
Dridi
> --
>
>

Eduardo Macarron

unread,
Jan 28, 2013, 10:03:16 AM1/28/13
to mybati...@googlegroups.com
@Felix.

Thanks for notifying. I will remove the @Override. I suppose I left
the dependency in my workspace to Spring 3.2 by mistake.

@Dridi

Those that did not inject the factory explicitly will have an startup
error. Easy to diagnose and to fix though.

1.2 version is the right moment for a change.

Eduardo Macarron

unread,
Jan 28, 2013, 10:08:38 AM1/28/13
to mybati...@googlegroups.com
Felix, my pom is right, it points to spring 3.1.3. What exception you had?

2013/1/28 Eduardo Macarron <eduardo....@gmail.com>:
Reply all
Reply to author
Forward
0 new messages