ResultMaps : Injecting collection without having any getter

205 views
Skip to first unread message

Frédéric Camblor

unread,
Oct 13, 2012, 7:50:29 AM10/13/12
to mybati...@googlegroups.com
Hi folks !

I have a relatively simple case, something like this :

public class Foo {
    private List<Bar> bars;

    public void List<Bar> getBars(){
        return this.bars;
    }

    // I don't want to provide a Bars setter to the outside of my bean
    // I only want to create bars from quixes I retrieve from db
    public void setQuixes(List<Quix> quixes){
        this.bars = convertQuixesToBars(quixes);
    }
    
    private List<Bar> convertQuixesToBars(List<Quix> quixes){ ... }
}

In my mapping file, I'll have something like this :
<resultMap id="Quix" type="foo.Quix">....</resultMap>
<resultMap id="Foo" type="foo.Foo">
    <collection property="quixes" resultMap="Quix" columnPrefix="quixes." />
</resultMap>

When mapping occurs, I catch an exception :
There is no getter for property named 'quixes' in 'class foo.Foo

Seems like MyBatis needs a collection getter in order, I suppose, to successively call add() on it.
I was just wondering : would there be a way to tell Mybatis to create the collection "externally" and the inject it via the setter method only (without calling the getQuixes() any time) ?

Thanks in advance,

Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter


Frédéric Camblor

unread,
Oct 17, 2012, 3:22:10 AM10/17/12
to mybati...@googlegroups.com
No advice about this topic ?

FTM, I only found 2 workarounds :
  • Creating a new DAO-layer-dedicated POJO having a List<Quix> inside it, and converting it to my Foo instance after db retrieval. 
    Why not but heh, you know it's kinda boring to not forget to make this POJO evolve every time Foo evolves.

  • Creating an ugly Foo subclass having an anonymous ArrayList<Quix> subclass having the add() method overriden which populate the List<Bar> on every add.
    Note that I _had_ to instantiate this ArrayList in the constructor and never instantiate it after because MyBatis seems to act "externally" with collections, like this :
    Foo createdFoo = new Foo();
    List<Quix> quixes = ....
    createdFoo.setQuixes(quixes);
    createdFoo.getQuixes().add(convertRowToQuix());
    createdFoo.getQuixes().add(convertRowToQuix());

    Instead of acting "on its side" like this :
    Foo createdFoo = new Foo();
    List<Quix> quixes = ....
    quixes.add(convertRowToQuix());
    quixes.add(convertRowToQuix());
    createdFoo.set(quixes);

Am I wrong ? (I would love to be wrong ! :-))


Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter




Poitras Christian

unread,
Oct 17, 2012, 9:03:43 AM10/17/12
to mybati...@googlegroups.com

Hi,

 

As a workaround, you can add a private getter method.

 

Christian

 

De : mybati...@googlegroups.com [mailto:mybati...@googlegroups.com] De la part de Frédéric Camblor
Envoyé : October-17-12 3:22 AM
À : mybati...@googlegroups.com
Objet : Re: ResultMaps : Injecting collection without having any getter

 

No advice about this topic ?

 

FTM, I only found 2 workarounds :

  • Creating a new DAO-layer-dedicated POJO having a List<Quix> inside it, and converting it to my Foo instance after db retrieval. 
    Why not but heh, you know it's kinda boring to not forget to make this POJO evolve every time Foo evolves.
  • Creating an ugly Foo subclass having an anonymous ArrayList<Quix> subclass having the add() method overriden which populate the List<Bar> on every add.
    Note that I _had_ to instantiate this ArrayList in the constructor and never instantiate it after because MyBatis seems to act "externally" with collections, like this :
    Foo createdFoo = new Foo();
    List<Quix> quixes = ....
    createdFoo.setQuixes(quixes);
    createdFoo.getQuixes().add(convertRowToQuix());
    createdFoo.getQuixes().add(convertRowToQuix());

    Instead of acting "on its side" like this :
    Foo createdFoo = new Foo();
    List<Quix> quixes = ....
    quixes.add(convertRowToQuix());
    quixes.add(convertRowToQuix());
    createdFoo.set(quixes);

 

Am I wrong ? (I would love to be wrong ! :-))

 


Frédéric Camblor Image supprimée par l'expéditeur. Image supprimée par l'expéditeur.

Bordeaux JUG Board member

Jenkins community member & plugin commiter

 



On Sat, Oct 13, 2012 at 1:50 PM, Frédéric Camblor <fcam...@gmail.com> wrote:

Hi folks !

 

I have a relatively simple case, something like this :

 

public class Foo {

    private List<Bar> bars;

 

    public void List<Bar> getBars(){

        return this.bars;

    }

 

    // I don't want to provide a Bars setter to the outside of my bean

    // I only want to create bars from quixes I retrieve from db

    public void setQuixes(List<Quix> quixes){

        this.bars = convertQuixesToBars(quixes);

    }

    

    private List<Bar> convertQuixesToBars(List<Quix> quixes){ ... }

}

 

In my mapping file, I'll have something like this :

<resultMap id="Quix" type="foo.Quix">....</resultMap>

<resultMap id="Foo" type="foo.Foo">

    <collection property="quixes" resultMap="Quix" columnPrefix="quixes." />

</resultMap>

 

When mapping occurs, I catch an exception :

There is no getter for property named 'quixes' in 'class foo.Foo

 

Seems like MyBatis needs a collection getter in order, I suppose, to successively call add() on it.

I was just wondering : would there be a way to tell Mybatis to create the collection "externally" and the inject it via the setter method only (without calling the getQuixes() any time) ?

 

Thanks in advance,


Frédéric Camblor Image supprimée par l'expéditeur. Image supprimée par l'expéditeur.

Poitras Christian

unread,
Oct 17, 2012, 9:08:04 AM10/17/12
to mybati...@googlegroups.com

Actually MyBatis can use any private getter/setter.

MyBatis can also handle fields (even private) that have no getter/setter, POJOs for example.

 

Christian

 

De : mybati...@googlegroups.com [mailto:mybati...@googlegroups.com] De la part de Poitras Christian
Envoyé : October-17-12 9:04 AM
À : 'mybati...@googlegroups.com'
Objet : RE: ResultMaps : Injecting collection without having any getter

Frédéric Camblor

unread,
Oct 17, 2012, 5:00:30 PM10/17/12
to mybati...@googlegroups.com
Yup I already tried adding a private getter.

Problem is, as I said in my previous example, it _seems_ that setter is only used to inject an empty collection, getter is then called to add() rows on this collection.
=> Going this way, setter won't have the collection filled completely (so I cannot fill my List<Bar> field in the setter).

Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter

image001.jpg

Jeff Butler

unread,
Oct 17, 2012, 5:40:20 PM10/17/12
to mybati...@googlegroups.com
That's correct.  This is the way MyBatis works - it processes the result set row by row and does not build any "side" collections.

We've always said that the best way to keep out of trouble is to make your MyBatis objects very simple POJOs - as simple as possible.  This can, unfortunately, lead to an AnemicDomainModel antipattern, but it is the way MyBatis works and it would be difficult to change.

If you want a rich model, it is best to have some kind of translate layer between MyBatis POJOs and your rich model.

Jeff Butler
image001.jpg

Frank Martínez

unread,
Oct 17, 2012, 5:57:52 PM10/17/12
to mybati...@googlegroups.com
Hi Frédéric,

What about a ResultHandler?




--
Frank D. Martínez M.

image001.jpg

Frédéric Camblor

unread,
Oct 18, 2012, 3:21:57 AM10/18/12
to mybati...@googlegroups.com
@Jeff Ok I understand completely the anemic domain model way of thinking, and I agree changing current MyBatis behaviour on Collections would be harmful (especially for backward compat).
But I don't really catch why "external add" was made instead of building a side collection and injecting it in the end.
It seems really _unnatural_ behaviour compared to other introspection oriented frameworks (such as spring, jackson, hibernate and so on...)
I'll live with this thought.

@Frank ResultHandler will surely help to remove my ugly ArrayList.add() overriding behaviour in my collection, it's a good point.
Nevertheless it won't solve the problem (I'll still have to live with a mybatis-specific model in that case).

Thanks for answers.

Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter

image001.jpg

bryan hunt

unread,
Oct 18, 2012, 7:02:45 AM10/18/12
to mybati...@googlegroups.com
"I understand completely the anemic domain model way of thinking".

ROFL. Never thought I'd hear that sound-bite again.

I thought the SpringSource consultancy were about the only people promoting the Rich Domain Model 'pattern', and that was about 6 years ago. I've seen it in practice a couple of times, and it's not very pretty.

The thing about spaghetti is - try as you may to slurp it up, in a genteel manner. There's always that rogue strand which escapes the fork/spoon combination and ruins a perfectly good shirt.

Anaemic Domain Model, anti-pattern. Hmm, not sure I'd call it an anti-pattern. Call it logical separation of concern.






Frédéric Camblor

unread,
Oct 18, 2012, 12:00:31 PM10/18/12
to mybati...@googlegroups.com
Uh ? What the point here ?
Where did I speak about anemic domain "anti pattern" ?

I'm using anemic POJO currently but heh, you never had any calculated field in your anemic POJOs ?

I mean... something like this :
public class AnAnemicPOJO {
    private String field1, field2, field3, field4, ......;
    private List<Foo> foos;
    private Map<String, Foo> foosByName = new HashMap<>();

    /* here a bunch of getters/setters for fieldX and foos */

    // A particular case...
    public void setFoos(List<Foo> foos){
        this.foos.addAll(foos);
        for(Foo f: foos){
            this.foosByName.put(f.getName(), f);
        }
    }

    public Foo getFoo(String name){
        // Will avoid iterating over the complete list which could be very long in some cases...
        return foosByName.get(name);
    }
}

Don't you call this an anemic model ?
To my POV, this is an anemic model _even if_ there is a bit of code inside it (but heh, that's not business code... just code to speed things up).

So, if your position is to say that I should have 2 models, duplicating every "fieldX" fields, in respect to "separation of concerns", I personally find it overkill and unmaintainable on the long term (because I'll have to bind every field somewhere, and ensure when I add a new field, I don't miss to bind it)

But once again, I'll live with it since I love philosophy of MyBatis.

Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter


Dridi Boukelmoune

unread,
Oct 18, 2012, 12:07:32 PM10/18/12
to mybati...@googlegroups.com
Hi Frédéric,

Could "package-private" visibility be an acceptable compromise ?

Dridi

On Thu, Oct 18, 2012 at 6:00 PM, Frédéric Camblor <fcam...@gmail.com> wrote:
>
> Uh ? What the point here ?
> Where did I speak about anemic domain "anti pattern" ?
>
> I'm using anemic POJO currently but heh, you never had any calculated field in your anemic POJOs ?
>
> I mean... something like this :
> public class AnAnemicPOJO {
> private String field1, field2, field3, field4, ......;
> private List<Foo> foos;
> private Map<String, Foo> foosByName = new HashMap<>();
>
> /* here a bunch of getters/setters for fieldX and foos */
>
> // A particular case...
> public void setFoos(List<Foo> foos){
> this.foos.addAll(foos);
> for(Foo f: foos){
> this.foosByName.put(f.getName(), f);
> }
> }
>
> public Foo getFoo(String name){
> // Will avoid iterating over the complete list which could be very long in some cases...
> return foosByName.get(name);
> }
> }
>
> Don't you call this an anemic model ?
> To my POV, this is an anemic model _even if_ there is a bit of code inside it (but heh, that's not business code... just code to speed things up).
>
> So, if your position is to say that I should have 2 models, duplicating every "fieldX" fields, in respect to "separation of concerns", I personally find it overkill and unmaintainable on the long term (because I'll have to bind every field somewhere, and ensure when I add a new field, I don't miss to bind it)
>
> But once again, I'll live with it since I love philosophy of MyBatis.
>
> Frédéric Camblor
> Bordeaux JUG Board member
> Jenkins community member & plugin commiter
>
>
> On Thu, Oct 18, 2012 at 1:02 PM, bryan hunt <sentimen...@gmail.com> wrote:
>>
>> "I understand completely the anemic domain model way of thinking".
>>
>> ROFL. Never thought I'd hear that sound-bite again.
>>
>> I thought the SpringSource consultancy were about the only people promoting the Rich Domain Model 'pattern', and that was about 6 years ago. I've seen it in practice a couple of times, and it's not very pretty.
>>
>> The thing about spaghetti is - try as you may to slurp it up, in a genteel manner. There's always that rogue strand which escapes the fork/spoon combination and ruins a perfectly good shirt.
>>
>> Anaemic Domain Model, anti-pattern. Hmm, not sure I'd call it an anti-pattern. Call it logical separation of concern.
>>
>>
>>
>>
>>
>>
>



--
Dridi Boukelmoune
Développeur/Formateur

GSM : +33 (0)6 17 91 14 23

Frédéric Camblor

unread,
Oct 18, 2012, 1:00:21 PM10/18/12
to mybati...@googlegroups.com
Hi Dridi,

My problem has nothing to do with visibility (or maybe I don't catch your point, if so, could you elaborate ?).
Problem is due to collections "injected" by MyBatis : they are *always* empty when passed to the setter.
MyBatis will fille them _after calling the setter_, by calling getFoos().add(_internallyMapRowToFoo(currentRow));

Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter


Dridi Boukelmoune

unread,
Oct 19, 2012, 5:12:31 AM10/19/12
to mybati...@googlegroups.com
You could have a default visibility pair of getter/setter that would _bypass_ your regular public getter/setter which have logic inside.

///
public List<?> getRegular() {
  // the implementation you seek
}

public void setRegular(List<?>) {
  // the implementation you seek
}

List<?> getShortcut() {
  // return the instance MB will add into
}

void setShortcut(List<?>) {
  // get the instance MB will populate
  // initialize stuff
  // maybe add a decorator to the MB list
}
///

If you decorate the list MB will set, you can enhance its behaviour (for instance update foosByName when an element is added).

You could then _expose the public API as intended_, and have some "invisible" API that should _only be used by trusted parties_ like MyBatis and unit tests. A bit boilerplate-ish but feasible.

Dridi

Frédéric Camblor

unread,
Oct 19, 2012, 8:37:49 AM10/19/12
to mybati...@googlegroups.com
Yup this is exactly what I meant in my second workaround but I'm not a big fan of subclassing ArrayList to decorate add() methods just for this (it is what I did but heh, not really proud of it).

Frédéric Camblor  
Bordeaux JUG Board member
Jenkins community member & plugin commiter


Reply all
Reply to author
Forward
0 new messages