selectByExample is ambiguous in Mapped Statements collection

1,395 views
Skip to first unread message

Riccardo

unread,
Sep 16, 2010, 8:34:15 AM9/16/10
to mybatis-user
Hello,
I'm trying to write a "generic" dao for different tables which may
share a few columns. I defined a hierarchy of mappers which reflects
this table structure (that is I have a BaseMapper which defines a
"selectByExample" query, which is overridden by specific mappers).
All mappers have their own different namespace and this is confermed
because debugging the session configuration I see that
"selectByExample" is bound in different namespaces.

I'm trying to execute this statement:

U mapper = session.getMapper(namespace);
List<T> list = mapper.selectByExample(example);

where <T> is declared as <T extends BaseBean>, U as <U extends
BaseBeanMapper<T>>, namespace is a Class<U> and example is an
instance of <T>.

I would thik that session.getMapper would return me a Mapper where
selectByExample is bound to the specific namespace, but this isn't the
case, I get this exception:

java.lang.IllegalArgumentException: selectByExample is ambiguous in
Mapped Statements collection (try using the full name including the
namespace, or rename one of the entries)
at org.apache.ibatis.session.Configuration
$StrictMap.get(Configuration.java:466)
at
org.apache.ibatis.session.Configuration.getMappedStatement(Configuration.java:
349)
at
org.apache.ibatis.binding.MapperMethod.setupCommandType(MapperMethod.java:
137)
at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:
46)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:34)
at $Proxy6.selectByExample(Unknown Source)

I'm using mybatis 3.0.2 on java 1.5, with java configuration (not
XML), using the "companion XML mapping" pattern described in the user
guide; the mapping files are like this, where like I said the
namespace part is different and unique for every mapper:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="avvocati24.StatoMapper">
<resultMap id="detailStato" type="Stato">
<result property="id" column="idStato" />
</resultMap>
<select id="selectByExample" parameterType="Stato"
resultMap="detailStato">
select * from Stato
<where>
<if test="id != null">
idStato = #{id}
</if>
</where>
</select>
<select id="selectById" parameterType="int" resultMap="detailStato">
select *
from Stato
WHERE idStato = #{id}
</select>
</mapper>

What is the correct way to address my needs?

Thanks for any help,
Riccardo

Riccardo

unread,
Sep 17, 2010, 8:57:19 AM9/17/10
to mybatis-user
In the end I found that there was no need in my project for such a
thing, I think it's mostly a hibernate/jpa attitude that drives that
way (one class per table).

But if anyone knows the answer I would be glad to hear :-)

Thanks,
Riccardo

Clinton Begin

unread,
Sep 17, 2010, 11:49:00 AM9/17/10
to mybati...@googlegroups.com
I read the original post and sounds like something that should work... but if I recall, when you register a mapper, it gets registered under 2 names:  The fully qualified name, and the short name.  So if you have two methods with the same name, then they'll have to be fully qualified when you call them.  The hard thing for me to understand was how you were calling it. 

Maybe if you get 10 minutes, throw a simple unit test together and file it in the issue tracker.

Clinton

Tim

unread,
Sep 17, 2010, 12:11:16 PM9/17/10
to mybati...@googlegroups.com
I have something that sounds extremely similar to you.
I have a method defined as:

@NotNull
public <T, M extends StaticDataDao<T>> List<T> selectAll(@NotNull
Class<M> aMapperType) {
final SqlSession mySession = theSessionFactory.openSession();
try {
final M myMapper = mySession.getMapper(aMapperType);
return myMapper.selectAll();
} finally {
mySession.close();
}
}

The trick here is that each dao MUST implement the selectAll in their
respective mapper xmls (or in their annotations) IFF (if and only if
for those that weren't comp sci majors) you have more than one mapper
already defining that method. Unless you are using it specifically
from the namespaces defined with the selectAlls

So to recap.
If I had 3 mappers A, B, C
And A had
<select id="selectAll" resultMap="SomeMap">
<include refid="findAllSQL"/> <!-- don't be too concerned with
the syntax here -->
</select>
In its namespace then it will work for ALL combinations of:
A.selectAll
B.selectAll
C.selectAll
selectAll

If I had A AND B mappers defined with that then it would be:
A.selectAll - works
B.selectAll - works
C.selectAll - FAILS
selectAll - FAILS

I had a semi related blog post on this subject
http://blog.chengin.com/?p=32

Riccardo

unread,
Sep 20, 2010, 6:29:06 AM9/20/10
to mybatis-user
Hello Clinton and Tim,
I have uploaded a sample project to test the problem:
http://code.google.com/p/mybatis/issues/detail?id=112

I think the problem may be more a generics fault, than a specific
ibatis one, as proposed by Tim; don't know if ibatis could do anything
about it.
Or maybe I got the model wrong and someone can give me some hints on
how to get it right.

Thanks to you both anyway,
Riccardo
> I had a semi related blog post on this subjecthttp://blog.chengin.com/?p=32
>
>
>
> On Fri, Sep 17, 2010 at 10:49 AM, Clinton Begin <clinton.be...@gmail.com> wrote:
> > I read the original post and sounds like something that should work... but
> > if I recall, when you register a mapper, it gets registered under 2 names:
> >  The fully qualified name, and the short name.  So if you have two methods
> > with the same name, then they'll have to be fully qualified when you call
> > them.  The hard thing for me to understand was how you were calling it.
> > Maybe if you get 10 minutes, throw a simple unit test together and file it
> > in the issue tracker.
> > Clinton
>

Maximiliano Juárez Ramos

unread,
Sep 20, 2010, 10:26:36 PM9/20/10
to mybati...@googlegroups.com
I think you only have to change the namespace in 'UserMapper.xml'

-    namespace="generics.UserMapper">
+    namespace="com.ibatis.testcase.model.mapper.UserMapper">

saludos

Riccardo

unread,
Sep 21, 2010, 4:16:24 AM9/21/10
to mybatis-user
Thanks Maximiliano,
this way it works and actually using the mapper fully qualified name
in the xml mapping is advised at the end of page 7 of the user guide.
What I find strange and a little inconsistent is the fact that, given
there are no duplicate names (so in this case I would use findAllUsers
and findAllProducts instead of findAll) it all works ok using the
"shorthand" namespace (generics.UserMapper).
I find this short namespace very useful when referring mappers from
other namespaces, for the following reason: suppose I have an Invoice
which is related to both User and Product, I would define a mapping
for User in UserMapper.xml and do the same with Product; then I would
refer those mappings in the association mapping of Invoice:

<association resultMap="generics.UserMapper.userDetail"
property="user"..../>
<collection resultMap="generics.ProductMapper.productDetail"
property="products"..../>

Those mappings turn out to be a lot more readable than the ones with
fully qualified names and I don't see why this approach fails, given
the fact that mybatis is able to match those xml to the mapper class
even if their namespace is not equal to the class fully qualified
name.

Bye,
Riccardo
On 21 Set, 04:26, Maximiliano Juárez Ramos <juarez...@gmail.com>
wrote:
Reply all
Reply to author
Forward
0 new messages