Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

classic Classic list List threaded Threaded
21 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Rick R
Been out of the myBatis loop for a bit. I started with myBatis 3
several months back and the current code has most of its service calls
looking like:

public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
        SqlSession session =
MetaDbIbatisConfig.getSessionFactory().openSession();
        PromptMapper mapper = session.getMapper(PromptMapper.class);
        try {
                return mapper.findPromptsForPromptType(promptTypeID);
        } finally {
                session.close();
        }
}

That's just pretty ugly.

Is there some kind of support that the above can change to something
like:

//MyServiceClass

@sessionFactory(name="metaDBfactory")
SqlSession session;

public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
        PromptMapper mapper = session.getMapper(PromptMapper.class);
        return mapper.findPromptsForPromptType(promptTypeID);
}


Note, I know a lot of you are passing a session from a session
injecting via Guice per request in the web layer. In our case, our
"mybatis services jar" loads the SqlSessionFactory in a static block
from a commons.dbcp.BasicDatasource loaded from a properties file on
the class path. Anyone can use the jar as long as they provide a valid
properties files on their class path. It seems pretty clean, the only
drawback is all of the above clutter where we are constantly getting a
Session from the factory and manually having to remember to call
close()


Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Larry Meadors
Use SqlSessionManager instead. It behaves in a MUCH simpler to use manner.

Larry


On Mon, Jan 24, 2011 at 1:37 PM, rickcr <[hidden email]> wrote:

> Been out of the myBatis loop for a bit. I started with myBatis 3
> several months back and the current code has most of its service calls
> looking like:
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>        SqlSession session =
> MetaDbIbatisConfig.getSessionFactory().openSession();
>        PromptMapper mapper = session.getMapper(PromptMapper.class);
>        try {
>                return mapper.findPromptsForPromptType(promptTypeID);
>        } finally {
>                session.close();
>        }
> }
>
> That's just pretty ugly.
>
> Is there some kind of support that the above can change to something
> like:
>
> //MyServiceClass
>
> @sessionFactory(name="metaDBfactory")
> SqlSession session;
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>        PromptMapper mapper = session.getMapper(PromptMapper.class);
>        return mapper.findPromptsForPromptType(promptTypeID);
> }
>
>
> Note, I know a lot of you are passing a session from a session
> injecting via Guice per request in the web layer. In our case, our
> "mybatis services jar" loads the SqlSessionFactory in a static block
> from a commons.dbcp.BasicDatasource loaded from a properties file on
> the class path. Anyone can use the jar as long as they provide a valid
> properties files on their class path. It seems pretty clean, the only
> drawback is all of the above clutter where we are constantly getting a
> Session from the factory and manually having to remember to call
> close()
>
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Clinton Begin
Administrator
If you like the 2.x way of doing things, yes, SqlSessionManager is the way to go.  It worked well for years, and will continue to do so.  3.x was designed to be more friendly for dependency injection -- which 2.x was definitely not friendly toward. 

One thing I've noticed here is that you're actually proxying the interface to avoid the "boilerplate" session/mapper code.  That's a clear sign that you should really check out a DI solution, perhaps the Guice sub-project (or just a servlet filter + request scope attribute works too).  

For example, here's a similar Stripes Action from one of my projects:

  @Inject
  public SqlSession sqlSession;
  @XMLBody
  public List<Story> stories;
  ...
  public Resolution list() {
    final StoryMapper storyMapper = sqlSession.getMapper(StoryMapper.class);
    stories = storyMapper.selectLightUserStoriesForProject(story.getProjectId());
    return new XMLResolution();
  }

All session management, exception handling is taken care of by the DI container and a servlet filter.  The validation of "story" is of course taken care of by Stripes.  I didn't inject the Mapper because I like having access to the SqlSession, but that said, I suppose I could have just injected both!  If you just inject the mapper, you have the added bonus of never seeing the ibatis.apache.org namespace in your other classes.  Doing so would take it down to:

  @Inject
  public StoryMapper storyMapper;
  @XMLBody
  public List<Story> stories;
  ...
  public Resolution list() {
    stories = storyMapper.selectLightUserStoriesForProject(story.getProjectId());
    return new XMLResolution();
  }


If you decide to stick with what you have, here's a modified version of what you posted...

// No need calling this in the method body every time 
// (I recommend putting it in the constructor body and wrapping it with proper error checking)
private SqlSessionFactory sqlSessionFactory = MetaDbIbatisConfig.getSessionFactory();

public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
    SqlSession session = sqlSessionFactory.openSession();
    try {
        // Make sure to put this inside the try block as well.
        PromptMapper mapper = session.getMapper(PromptMapper.class);
        return mapper.findPromptsForPromptType(promptTypeID);
    } finally {
        session.close();
    }
}


Cheers,
Clinton

On Mon, Jan 24, 2011 at 3:59 PM, Larry Meadors <[hidden email]> wrote:
Use SqlSessionManager instead. It behaves in a MUCH simpler to use manner.

Larry


On Mon, Jan 24, 2011 at 1:37 PM, rickcr <[hidden email]> wrote:
> Been out of the myBatis loop for a bit. I started with myBatis 3
> several months back and the current code has most of its service calls
> looking like:
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>        SqlSession session =
> MetaDbIbatisConfig.getSessionFactory().openSession();
>        PromptMapper mapper = session.getMapper(PromptMapper.class);
>        try {
>                return mapper.findPromptsForPromptType(promptTypeID);
>        } finally {
>                session.close();
>        }
> }
>
> That's just pretty ugly.
>
> Is there some kind of support that the above can change to something
> like:
>
> //MyServiceClass
>
> @sessionFactory(name="metaDBfactory")
> SqlSession session;
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>        PromptMapper mapper = session.getMapper(PromptMapper.class);
>        return mapper.findPromptsForPromptType(promptTypeID);
> }
>
>
> Note, I know a lot of you are passing a session from a session
> injecting via Guice per request in the web layer. In our case, our
> "mybatis services jar" loads the SqlSessionFactory in a static block
> from a commons.dbcp.BasicDatasource loaded from a properties file on
> the class path. Anyone can use the jar as long as they provide a valid
> properties files on their class path. It seems pretty clean, the only
> drawback is all of the above clutter where we are constantly getting a
> Session from the factory and manually having to remember to call
> close()
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Rick R
On Mon, Jan 24, 2011 at 7:46 PM, Clinton Begin <[hidden email]> wrote:
If you like the 2.x way of doing things, yes, SqlSessionManager is the way to go.  It worked well for years, and will continue to do so.  3.x was designed to be more friendly for dependency injection -- which 2.x was definitely not friendly toward. 

Actually this class works perfectly! I was glad to hear it existed (looks like it replaces the old SqlMapClient stuff.)  I didn't see it mentioned in the PDF doc when I read through it the first time, but maybe I missed it?

I know every is all excited over the DI stuff but in our case it really wasn't bring that much to the table. I'll look more closely at the Guice sub-project some time in the future.
 
 
  public Resolution list() {
    final StoryMapper storyMapper = sqlSession.getMapper(StoryMapper.class);
    stories = storyMapper.selectLightUserStoriesForProject(story.getProjectId());
    return new XMLResolution();
  }


I don't really like the above much in regard to using mappers directly in Stripes ActionBeans. I still like keeping the ibatis calls behind a Service class so that the web layer isn't even aware of the db persistence choice. Also, I sometimes need to massage the data a bit before even returning XML so it makes sense to do that in one place.

 

// No need calling this in the method body every time 
// (I recommend putting it in the constructor body and wrapping it with proper error checking)
private SqlSessionFactory sqlSessionFactory = MetaDbIbatisConfig.getSessionFactory();

public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
    SqlSession session = sqlSessionFactory.openSession();
    try {
        // Make sure to put this inside the try block as well.
        PromptMapper mapper = session.getMapper(PromptMapper.class);
        return mapper.findPromptsForPromptType(promptTypeID);
    } finally {
        session.close();
    }
}



Even cleaner using the SqlSessionMapper:

public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
    PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
        .getMapper(PromptMapper.class);
    return mapper.findPromptsForPromptType(promptTypeID);
}
 


Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Clinton Begin
Administrator
I totally understand.  For me, two years of rails development was
enough to discourage me from the  typical "7-layer java cake" design I
used for years.

Action calls service, service calls dao, dao calls mapper, service
also uses business logic helpers, domain package, then a bunch of
proxies and middleware pieces...  And that's without spring or ejb!
:-)

I figure there are way too many successful flat rails and php projects
for all of that to be necessary just because our language is java.

So I've started to take the opposite approach.  I introduce layers of
abstraction where and when they are necessary -- but I also look for
alternatives to layers if possible.

Cheers,
Clinton

On 2011-01-24, Rick R <[hidden email]> wrote:

> On Mon, Jan 24, 2011 at 7:46 PM, Clinton Begin
> <[hidden email]>wrote:
>
>> If you like the 2.x way of doing things, yes, SqlSessionManager is the way
>> to go.  It worked well for years, and will continue to do so.  3.x was
>> designed to be more friendly for dependency injection -- which 2.x was
>> definitely not friendly toward.
>
>
> Actually this class works perfectly! I was glad to hear it existed (looks
> like it replaces the old SqlMapClient stuff.)  I didn't see it mentioned in
> the PDF doc when I read through it the first time, but maybe I missed it?
>
> I know every is all excited over the DI stuff but in our case it really
> wasn't bring that much to the table. I'll look more closely at the Guice
> sub-project some time in the future.
>
>
>>
>>   public Resolution list() {
>>     final StoryMapper storyMapper =
>> sqlSession.getMapper(StoryMapper.class);
>>     stories =
>> storyMapper.selectLightUserStoriesForProject(story.getProjectId());
>>     return new XMLResolution();
>>   }
>>
>>
> I don't really like the above much in regard to using mappers directly in
> Stripes ActionBeans. I still like keeping the ibatis calls behind a Service
> class so that the web layer isn't even aware of the db persistence choice.
> Also, I sometimes need to massage the data a bit before even returning XML
> so it makes sense to do that in one place.
>
>
>>
>> // No need calling this in the method body every time
>> // (I recommend putting it in the constructor body and wrapping it with
>> proper error checking)
>> private SqlSessionFactory sqlSessionFactory = MetaDbIbatisConfig.
>> getSessionFactory();
>>
>> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>>     SqlSession session = sqlSessionFactory.openSession();
>>     try {
>>         // Make sure to put this inside the try block as well.
>>         PromptMapper mapper = session.getMapper(PromptMapper.class);
>>         return mapper.findPromptsForPromptType(promptTypeID);
>>     } finally {
>>         session.close();
>>     }
>> }
>>
>>
> Even cleaner using the SqlSessionMapper:
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>     PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
>         .getMapper(PromptMapper.class);
>     return mapper.findPromptsForPromptType(promptTypeID);
> }
>

--
Sent from my mobile device
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Larry Meadors
In reply to this post by Rick R
On Mon, Jan 24, 2011 at 8:18 PM, Rick R <[hidden email]> wrote:
> Even cleaner using the SqlSessionMapper:
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>     PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
>         .getMapper(PromptMapper.class);
>     return mapper.findPromptsForPromptType(promptTypeID);
> }

Actually, Rick, you can make that even simpler - the managed mappers
don't need to be retrieved every time, so you can make them members on
your class, set them them in the constructor and then your method is
just this:

public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
  return mapper.findPromptsForPromptType(promptTypeID);
}

Also, if you pass the SSM to the constructor for that class, your
tests are simpler too because you can mock the SSM when you create the
test instance, and it all just works. :)

I inject all of my mappers this way, it makes for lots less code -
just add an @Inject annotation to the constructor.

Larry
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Michael
Yes. I agree with Larry. Inject mapper class directly. Accompany with
@Transactional, It will 'cleaning up' all this Session get/close
clutter. Because the AOP will handle it for you.

On Jan 25, 9:03 pm, Larry Meadors <[hidden email]> wrote:

> On Mon, Jan 24, 2011 at 8:18 PM, Rick R <[hidden email]> wrote:
> > Even cleaner using the SqlSessionMapper:
>
> > public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
> >     PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
> >         .getMapper(PromptMapper.class);
> >     return mapper.findPromptsForPromptType(promptTypeID);
> > }
>
> Actually, Rick, you can make that even simpler - the managed mappers
> don't need to be retrieved every time, so you can make them members on
> your class, set them them in the constructor and then your method is
> just this:
>
> public List<Prompt> findPromptsForPromptType(Integer promptTypeID) {
>   return mapper.findPromptsForPromptType(promptTypeID);
>
> }
>
> Also, if you pass the SSM to the constructor for that class, your
> tests are simpler too because you can mock the SSM when you create the
> test instance, and it all just works. :)
>
> I inject all of my mappers this way, it makes for lots less code -
> just add an @Inject annotation to the constructor.
>
> Larry
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Guy Rouillier
In reply to this post by Larry Meadors
On 1/25/2011 8:03 AM, Larry Meadors wrote:

> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]>  wrote:
>> Even cleaner using the SqlSessionMapper:
>>
>> public List<Prompt>  findPromptsForPromptType(Integer promptTypeID) {
>>      PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
>>          .getMapper(PromptMapper.class);
>>      return mapper.findPromptsForPromptType(promptTypeID);
>> }
>
> Actually, Rick, you can make that even simpler - the managed mappers
> don't need to be retrieved every time, so you can make them members on
> your class, set them them in the constructor and then your method is
> just this:

I don't understand how that works.  A session is a wrapper around a
Connection.  I've always assumed that you get a mapper from a session
object so that the mapper has access to the wrapped connection.  If you
store a reference to a mapper and then close the session, how does the
mapper associated itself with a new session (and hence connection) the
next time you retrieve one?

--
Guy Rouillier
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Larry Meadors
Your mappers don't have a reference to a session, they just delegate to one.

So, in this case, the SqlSessionManager creates and destroys the
sessions for you - they are stored either as local variables or on a
ThreadLocal, depending on how you call the methods.

The code's actually pretty simple - check it out. :)

Larry


On Tue, Jan 25, 2011 at 9:45 PM, Guy Rouillier <[hidden email]> wrote:

> On 1/25/2011 8:03 AM, Larry Meadors wrote:
>>
>> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]>  wrote:
>>>
>>> Even cleaner using the SqlSessionMapper:
>>>
>>> public List<Prompt>  findPromptsForPromptType(Integer promptTypeID) {
>>>     PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
>>>         .getMapper(PromptMapper.class);
>>>     return mapper.findPromptsForPromptType(promptTypeID);
>>> }
>>
>> Actually, Rick, you can make that even simpler - the managed mappers
>> don't need to be retrieved every time, so you can make them members on
>> your class, set them them in the constructor and then your method is
>> just this:
>
> I don't understand how that works.  A session is a wrapper around a
> Connection.  I've always assumed that you get a mapper from a session object
> so that the mapper has access to the wrapped connection.  If you store a
> reference to a mapper and then close the session, how does the mapper
> associated itself with a new session (and hence connection) the next time
> you retrieve one?
>
> --
> Guy Rouillier
>
Tim
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Tim
And it's a pretty nifty use of the proxy feature in java.
This is how I've been doing it since Larry showed it to me a while
back and it's been great.
Now I just @Inject my mappers in the constructors for my services that
use them and voila.
So much cleaner for code and for test.

On Wed, Jan 26, 2011 at 8:57 AM, Larry Meadors <[hidden email]> wrote:

> Your mappers don't have a reference to a session, they just delegate to one.
>
> So, in this case, the SqlSessionManager creates and destroys the
> sessions for you - they are stored either as local variables or on a
> ThreadLocal, depending on how you call the methods.
>
> The code's actually pretty simple - check it out. :)
>
> Larry
>
>
> On Tue, Jan 25, 2011 at 9:45 PM, Guy Rouillier <[hidden email]> wrote:
>> On 1/25/2011 8:03 AM, Larry Meadors wrote:
>>>
>>> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]>  wrote:
>>>>
>>>> Even cleaner using the SqlSessionMapper:
>>>>
>>>> public List<Prompt>  findPromptsForPromptType(Integer promptTypeID) {
>>>>     PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
>>>>         .getMapper(PromptMapper.class);
>>>>     return mapper.findPromptsForPromptType(promptTypeID);
>>>> }
>>>
>>> Actually, Rick, you can make that even simpler - the managed mappers
>>> don't need to be retrieved every time, so you can make them members on
>>> your class, set them them in the constructor and then your method is
>>> just this:
>>
>> I don't understand how that works.  A session is a wrapper around a
>> Connection.  I've always assumed that you get a mapper from a session object
>> so that the mapper has access to the wrapped connection.  If you store a
>> reference to a mapper and then close the session, how does the mapper
>> associated itself with a new session (and hence connection) the next time
>> you retrieve one?
>>
>> --
>> Guy Rouillier
>>
>
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Rick R
Yea SqlSessionManager kicks ass.  I dug into the code to see how that proxy stuff was working. Pretty slick. "Mees got meself somes educations.":)

I'm still not leveraging DI for the stuff since the below seems clean enough, and the jar is going to be used by several other independent teams. (The only caveat is the team using the jar has to have an appropriate db properties file on their classpath. We have to leverage offshore for all this stuff so if we throw in one other thing like Spring or Guice it will end up sparking a billion meetings that I don't want to have to attend:)

private PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager().getMapper(PromptMapper.class);

public List<ReportPromptViewItem> findPromptViewItems(PromptViewItemsFilter filter) {
     return mapper.findPromptViewItems(filter);
 }

On Wed, Jan 26, 2011 at 10:33 AM, Tim <[hidden email]> wrote:
And it's a pretty nifty use of the proxy feature in java.
This is how I've been doing it since Larry showed it to me a while
back and it's been great.
Now I just @Inject my mappers in the constructors for my services that
use them and voila.
So much cleaner for code and for test.

On Wed, Jan 26, 2011 at 8:57 AM, Larry Meadors <[hidden email]> wrote:
> Your mappers don't have a reference to a session, they just delegate to one.
>
> So, in this case, the SqlSessionManager creates and destroys the
> sessions for you - they are stored either as local variables or on a
> ThreadLocal, depending on how you call the methods.
>
> The code's actually pretty simple - check it out. :)
>
> Larry
>
>
> On Tue, Jan 25, 2011 at 9:45 PM, Guy Rouillier <[hidden email]> wrote:
>> On 1/25/2011 8:03 AM, Larry Meadors wrote:
>>>
>>> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]>  wrote:
>>>>
>>>> Even cleaner using the SqlSessionMapper:
>>>>
>>>> public List<Prompt>  findPromptsForPromptType(Integer promptTypeID) {
>>>>     PromptMapper mapper =  MetaDbIbatisConfig.getSqlSessionManager()
>>>>         .getMapper(PromptMapper.class);
>>>>     return mapper.findPromptsForPromptType(promptTypeID);
>>>> }
>>>
>>> Actually, Rick, you can make that even simpler - the managed mappers
>>> don't need to be retrieved every time, so you can make them members on
>>> your class, set them them in the constructor and then your method is
>>> just this:
>>
>> I don't understand how that works.  A session is a wrapper around a
>> Connection.  I've always assumed that you get a mapper from a session object
>> so that the mapper has access to the wrapped connection.  If you store a
>> reference to a mapper and then close the session, how does the mapper
>> associated itself with a new session (and hence connection) the next time
>> you retrieve one?
>>
>> --
>> Guy Rouillier
>>
>



--
Rick R
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Guy Rouillier
I finally got a short break from long work days, and gave
SqlSessionManager a try.  I like that it takes care of the boilerplate
of opening and closing sessions for me.  However, reading through the
code I'm uncertain how it works.  The voodoo seems to be happening
inside this snippet in the SqlSessionManager constructor:

     this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
         SqlSessionFactory.class.getClassLoader(),
         new Class[]{SqlSession.class},
         new SqlSessionInterceptor());

I read through ssm.getMapper(), and I notice that a SqlSession is never
instantiated, even though it gets passed on through several additional
layers.  So, I'm guessing that SqlSession is instantiated at the moment
it is required, via the above Proxy.

I ran a short test on a local PostgreSQL database so I could watch open
connections.  I purposely use an UNPOOLED datasource so each connection
would be explicitly created.  I used my contact_manager_cli sample from
the issue reports.  mapper is an instance variable, and all the calls
that use it are in independent methods so I could watch things going in
and out of scope:

    mapper = ssm.getMapper(ContactMapper.class);
...
    private void getContactById()
       {
       HashMap<String, Integer> hmParams = new HashMap<String, Integer>();
       hmParams.put("id", 1);
       Contact c = mapper.selectById(hmParams);
       if (c != null)
          {
          log.info("Contact name: " + c.getFirstName() + " " +
c.getLastName());
          }
       }

What I found was that the connection count in PostgreSQL never
increased, even when single stepping through this method.  So, that
leads me to conclude that every time a mapper method is executed, a new
connection object is created, and then deleted at the end of the method.
  Is that correct?

That is nice in that it saves me a lot of work, but not very efficient.
  If I want to execute multiple mapper statements in a row, I want them
to all use the same connection.  How do I accomplish that?  Is that what
startManagedSession() is for?  Sorry for so many questions, but I can't
find documentation, and the JavaDoc is... sparse :).

I see that startManagedSession() is a wrapper around openSession().  If
I store a mapper in an instance variable as above, can I use
startManagedSession() and openSession() interchangeably and still have
the mapper variable remain valid?

I looked for some way to stop or end a managed session; by reading the
source, it appears that close() is used for that.  Since we start a
managed session rather than opening it, I'd suggest endManagedSession()
would be more easily recognizable than close().

Thanks.

On 1/26/2011 2:43 PM, Rick R wrote:

> Yea SqlSessionManager kicks ass.  I dug into the code to see how that
> proxy stuff was working. Pretty slick. "Mees got meself somes educations.":)
>
> I'm still not leveraging DI for the stuff since the below seems clean
> enough, and the jar is going to be used by several other independent
> teams. (The only caveat is the team using the jar has to have an
> appropriate db properties file on their classpath. We have to leverage
> offshore for all this stuff so if we throw in one other thing like
> Spring or Guice it will end up sparking a billion meetings that I don't
> want to have to attend:)
>
> private PromptMapper mapper =
> MetaDbIbatisConfig.getSqlSessionManager().getMapper(PromptMapper.class);
>
> public List<ReportPromptViewItem>
> findPromptViewItems(PromptViewItemsFilter filter) {
>       return mapper.findPromptViewItems(filter);
>   }
>
> On Wed, Jan 26, 2011 at 10:33 AM, Tim <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     And it's a pretty nifty use of the proxy feature in java.
>     This is how I've been doing it since Larry showed it to me a while
>     back and it's been great.
>     Now I just @Inject my mappers in the constructors for my services that
>     use them and voila.
>     So much cleaner for code and for test.
>
>     On Wed, Jan 26, 2011 at 8:57 AM, Larry Meadors
>     <[hidden email] <mailto:[hidden email]>> wrote:
>      > Your mappers don't have a reference to a session, they just
>     delegate to one.
>      >
>      > So, in this case, the SqlSessionManager creates and destroys the
>      > sessions for you - they are stored either as local variables or on a
>      > ThreadLocal, depending on how you call the methods.
>      >
>      > The code's actually pretty simple - check it out. :)
>      >
>      > Larry
>      >
>      >
>      > On Tue, Jan 25, 2011 at 9:45 PM, Guy Rouillier
>     <[hidden email] <mailto:[hidden email]>> wrote:
>      >> On 1/25/2011 8:03 AM, Larry Meadors wrote:
>      >>>
>      >>> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]
>     <mailto:[hidden email]>>  wrote:
>      >>>>
>      >>>> Even cleaner using the SqlSessionMapper:
>      >>>>
>      >>>> public List<Prompt>  findPromptsForPromptType(Integer
>     promptTypeID) {
>      >>>>     PromptMapper mapper =
>       MetaDbIbatisConfig.getSqlSessionManager()
>      >>>>         .getMapper(PromptMapper.class);
>      >>>>     return mapper.findPromptsForPromptType(promptTypeID);
>      >>>> }
>      >>>
>      >>> Actually, Rick, you can make that even simpler - the managed
>     mappers
>      >>> don't need to be retrieved every time, so you can make them
>     members on
>      >>> your class, set them them in the constructor and then your
>     method is
>      >>> just this:
>      >>
>      >> I don't understand how that works.  A session is a wrapper around a
>      >> Connection.  I've always assumed that you get a mapper from a
>     session object
>      >> so that the mapper has access to the wrapped connection.  If you
>     store a
>      >> reference to a mapper and then close the session, how does the
>     mapper
>      >> associated itself with a new session (and hence connection) the
>     next time
>      >> you retrieve one?
>      >>
>      >> --
>      >> Guy Rouillier
>      >>
>      >
>
>
>
>
> --
> Rick R


--
Guy Rouillier
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Clinton Begin
Administrator
>> I see that startManagedSession() is a wrapper around openSession().  
>> If I store a mapper in an instance variable as above, can I use startManagedSession() 
>> and openSession() interchangeably and still have the mapper variable remain valid?

Yes, that should be fine.  Try augmenting your test to wrap startManagedSession() and close() around your multiple statements.  You should see the difference.  By default, if you don't start a managed session, it will use the "auto-commit-like behavior" that iBATIS 2.x used to use as its primary auto-commit behavior (iBATIS 2 didn't support true autocommit, but simulated it). But that's all history.  SqlSessionManager makes MyBatis 3 seem more like iBATIS 2, but still way better.

As for endManagedSession(), I suppose that would make a fine alias for close(), and would take all of 8 seconds to implement.  You may want to file a feature request though, as otherwise it will get lost in this mailing list. :-)

Cheers,
Clinton

On Tue, Feb 1, 2011 at 3:35 PM, Guy Rouillier <[hidden email]> wrote:
I finally got a short break from long work days, and gave SqlSessionManager a try.  I like that it takes care of the boilerplate of opening and closing sessions for me.  However, reading through the code I'm uncertain how it works.  The voodoo seems to be happening inside this snippet in the SqlSessionManager constructor:

   this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
       SqlSessionFactory.class.getClassLoader(),
       new Class[]{SqlSession.class},
       new SqlSessionInterceptor());

I read through ssm.getMapper(), and I notice that a SqlSession is never instantiated, even though it gets passed on through several additional layers.  So, I'm guessing that SqlSession is instantiated at the moment it is required, via the above Proxy.

I ran a short test on a local PostgreSQL database so I could watch open connections.  I purposely use an UNPOOLED datasource so each connection would be explicitly created.  I used my contact_manager_cli sample from the issue reports.  mapper is an instance variable, and all the calls that use it are in independent methods so I could watch things going in and out of scope:

  mapper = ssm.getMapper(ContactMapper.class);
...
  private void getContactById()
     {
     HashMap<String, Integer> hmParams = new HashMap<String, Integer>();
     hmParams.put("id", 1);
     Contact c = mapper.selectById(hmParams);
     if (c != null)
        {
        log.info("Contact name: " + c.getFirstName() + " " + c.getLastName());
        }
     }

What I found was that the connection count in PostgreSQL never increased, even when single stepping through this method.  So, that leads me to conclude that every time a mapper method is executed, a new connection object is created, and then deleted at the end of the method.  Is that correct?

That is nice in that it saves me a lot of work, but not very efficient.  If I want to execute multiple mapper statements in a row, I want them to all use the same connection.  How do I accomplish that?  Is that what startManagedSession() is for?  Sorry for so many questions, but I can't find documentation, and the JavaDoc is... sparse :).

I see that startManagedSession() is a wrapper around openSession().  If I store a mapper in an instance variable as above, can I use startManagedSession() and openSession() interchangeably and still have the mapper variable remain valid?

I looked for some way to stop or end a managed session; by reading the source, it appears that close() is used for that.  Since we start a managed session rather than opening it, I'd suggest endManagedSession() would be more easily recognizable than close().

Thanks.


On 1/26/2011 2:43 PM, Rick R wrote:
Yea SqlSessionManager kicks ass.  I dug into the code to see how that
proxy stuff was working. Pretty slick. "Mees got meself somes educations.":)

I'm still not leveraging DI for the stuff since the below seems clean
enough, and the jar is going to be used by several other independent
teams. (The only caveat is the team using the jar has to have an
appropriate db properties file on their classpath. We have to leverage
offshore for all this stuff so if we throw in one other thing like
Spring or Guice it will end up sparking a billion meetings that I don't
want to have to attend:)

private PromptMapper mapper =
MetaDbIbatisConfig.getSqlSessionManager().getMapper(PromptMapper.class);

public List<ReportPromptViewItem>
findPromptViewItems(PromptViewItemsFilter filter) {
     return mapper.findPromptViewItems(filter);
 }

On Wed, Jan 26, 2011 at 10:33 AM, Tim <[hidden email]
<mailto:[hidden email]>> wrote:

   And it's a pretty nifty use of the proxy feature in java.
   This is how I've been doing it since Larry showed it to me a while
   back and it's been great.
   Now I just @Inject my mappers in the constructors for my services that
   use them and voila.
   So much cleaner for code and for test.

   On Wed, Jan 26, 2011 at 8:57 AM, Larry Meadors
   <[hidden email] <mailto:[hidden email]>> wrote:
    > Your mappers don't have a reference to a session, they just
   delegate to one.
    >
    > So, in this case, the SqlSessionManager creates and destroys the
    > sessions for you - they are stored either as local variables or on a
    > ThreadLocal, depending on how you call the methods.
    >
    > The code's actually pretty simple - check it out. :)
    >
    > Larry
    >
    >
    > On Tue, Jan 25, 2011 at 9:45 PM, Guy Rouillier
   <[hidden email] <mailto:[hidden email]>> wrote:
    >> On 1/25/2011 8:03 AM, Larry Meadors wrote:
    >>>
    >>> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]
   <mailto:[hidden email]>>  wrote:

    >>>>
    >>>> Even cleaner using the SqlSessionMapper:
    >>>>
    >>>> public List<Prompt>  findPromptsForPromptType(Integer
   promptTypeID) {
    >>>>     PromptMapper mapper =
     MetaDbIbatisConfig.getSqlSessionManager()
    >>>>         .getMapper(PromptMapper.class);
    >>>>     return mapper.findPromptsForPromptType(promptTypeID);
    >>>> }
    >>>
    >>> Actually, Rick, you can make that even simpler - the managed
   mappers
    >>> don't need to be retrieved every time, so you can make them
   members on
    >>> your class, set them them in the constructor and then your
   method is
    >>> just this:
    >>
    >> I don't understand how that works.  A session is a wrapper around a
    >> Connection.  I've always assumed that you get a mapper from a
   session object
    >> so that the mapper has access to the wrapped connection.  If you
   store a
    >> reference to a mapper and then close the session, how does the
   mapper
    >> associated itself with a new session (and hence connection) the
   next time
    >> you retrieve one?
    >>
    >> --
    >> Guy Rouillier
    >>
    >




--
Rick R


--
Guy Rouillier

Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Jakov Istuk
I have tested SqlSessionManager  against older SqlMapClient paradigm
and it is slower in execution.
Has anyone experienced those kind of problem?
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Simone Tripodi-2
I suspect that's because the

1) threadlocals checking
2) dynamic proxy generation

but once obtained the mapper it should run with same execution time, or not?
Simo

http://people.apache.org/~simonetripodi/
http://www.99soft.org/



On Wed, Feb 2, 2011 at 3:50 PM, Jakov Istuk <[hidden email]> wrote:
> I have tested SqlSessionManager  against older SqlMapClient paradigm
> and it is slower in execution.
> Has anyone experienced those kind of problem?
>
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Clinton Begin
Administrator
iBATIS SqlMapClient used an almost identical approach.  

There is no way to respond to Jakov's assertion, as there is no detail provided whatsoever.  

Jakov:  Can you post a suite of tests that demonstrate iBATIS 2.x and MyBatis 3.x performing operations against a Derby database (don't use an in-memory DB) in a way that shows a significant and meaningful difference in performance?  

If so, there may be something we can do about it. Otherwise, the answer is "no", we haven't experienced this.

Cheers,
Clinton

On Wed, Feb 2, 2011 at 10:13 AM, Simone Tripodi <[hidden email]> wrote:
I suspect that's because the

1) threadlocals checking
2) dynamic proxy generation

but once obtained the mapper it should run with same execution time, or not?
Simo

http://people.apache.org/~simonetripodi/
http://www.99soft.org/



On Wed, Feb 2, 2011 at 3:50 PM, Jakov Istuk <[hidden email]> wrote:
> I have tested SqlSessionManager  against older SqlMapClient paradigm
> and it is slower in execution.
> Has anyone experienced those kind of problem?
>

Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Guy Rouillier
In reply to this post by Guy Rouillier
No replies, so I'll ask again my most important question.  Is the
following a proper use of SqlSessionManager?  I tried it with PostgreSQL
and it worked, but I'm not sure if that is true in general.
Specifically, does SqlSessionManager.close() just close a previously
startManagedSession(), or does it close the SqlSessionManager manager
itself and no further use of it should be made?  Since the example below
works, it appears I can create it just once, and then
startManagedSession() and close() as often as necessary.  And of course,
outside of managed session, just use my mappers as necessary for single
statement invocations.

Thanks.


          SqlSessionManager  ssm = null;
          String aResource = "iBatisConfig.xml";
          Reader reader = Resources.getResourceAsReader(aResource);
          ssm = SqlSessionManager.newInstance(reader);
 
log.info(ssm.getConfiguration().getEnvironment().getDataSource().toString());

          mapper = ssm.getMapper(ContactMapper.class);
          ssm.startManagedSession();
          log.info("Calling getContactById...\n");
          getContactById();
          log.info("Calling getAllContacts...\n");
          getAllContacts();
          ssm.close();
          log.info("Calling getContactById after ssm.close()...\n");
          getContactById();
          log.info("All done!\n");

On 2/1/2011 5:35 PM, Guy Rouillier wrote:

> I finally got a short break from long work days, and gave
> SqlSessionManager a try. I like that it takes care of the boilerplate of
> opening and closing sessions for me. However, reading through the code
> I'm uncertain how it works. The voodoo seems to be happening inside this
> snippet in the SqlSessionManager constructor:
>
> this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
> SqlSessionFactory.class.getClassLoader(),
> new Class[]{SqlSession.class},
> new SqlSessionInterceptor());
>
> I read through ssm.getMapper(), and I notice that a SqlSession is never
> instantiated, even though it gets passed on through several additional
> layers. So, I'm guessing that SqlSession is instantiated at the moment
> it is required, via the above Proxy.
>
> I ran a short test on a local PostgreSQL database so I could watch open
> connections. I purposely use an UNPOOLED datasource so each connection
> would be explicitly created. I used my contact_manager_cli sample from
> the issue reports. mapper is an instance variable, and all the calls
> that use it are in independent methods so I could watch things going in
> and out of scope:
>
> mapper = ssm.getMapper(ContactMapper.class);
> ..
> private void getContactById()
> {
> HashMap<String, Integer> hmParams = new HashMap<String, Integer>();
> hmParams.put("id", 1);
> Contact c = mapper.selectById(hmParams);
> if (c != null)
> {
> log.info("Contact name: " + c.getFirstName() + " " + c.getLastName());
> }
> }
>
> What I found was that the connection count in PostgreSQL never
> increased, even when single stepping through this method. So, that leads
> me to conclude that every time a mapper method is executed, a new
> connection object is created, and then deleted at the end of the method.
> Is that correct?
>
> That is nice in that it saves me a lot of work, but not very efficient.
> If I want to execute multiple mapper statements in a row, I want them to
> all use the same connection. How do I accomplish that? Is that what
> startManagedSession() is for? Sorry for so many questions, but I can't
> find documentation, and the JavaDoc is... sparse :).
>
> I see that startManagedSession() is a wrapper around openSession(). If I
> store a mapper in an instance variable as above, can I use
> startManagedSession() and openSession() interchangeably and still have
> the mapper variable remain valid?
>
> I looked for some way to stop or end a managed session; by reading the
> source, it appears that close() is used for that. Since we start a
> managed session rather than opening it, I'd suggest endManagedSession()
> would be more easily recognizable than close().
>
> Thanks.
>
> On 1/26/2011 2:43 PM, Rick R wrote:
>> Yea SqlSessionManager kicks ass. I dug into the code to see how that
>> proxy stuff was working. Pretty slick. "Mees got meself somes
>> educations.":)
>>
>> I'm still not leveraging DI for the stuff since the below seems clean
>> enough, and the jar is going to be used by several other independent
>> teams. (The only caveat is the team using the jar has to have an
>> appropriate db properties file on their classpath. We have to leverage
>> offshore for all this stuff so if we throw in one other thing like
>> Spring or Guice it will end up sparking a billion meetings that I don't
>> want to have to attend:)
>>
>> private PromptMapper mapper =
>> MetaDbIbatisConfig.getSqlSessionManager().getMapper(PromptMapper.class);
>>
>> public List<ReportPromptViewItem>
>> findPromptViewItems(PromptViewItemsFilter filter) {
>> return mapper.findPromptViewItems(filter);
>> }
>>
>> On Wed, Jan 26, 2011 at 10:33 AM, Tim <[hidden email]
>> <mailto:[hidden email]>> wrote:
>>
>> And it's a pretty nifty use of the proxy feature in java.
>> This is how I've been doing it since Larry showed it to me a while
>> back and it's been great.
>> Now I just @Inject my mappers in the constructors for my services that
>> use them and voila.
>> So much cleaner for code and for test.
>>
>> On Wed, Jan 26, 2011 at 8:57 AM, Larry Meadors
>> <[hidden email] <mailto:[hidden email]>> wrote:
>> > Your mappers don't have a reference to a session, they just
>> delegate to one.
>> >
>> > So, in this case, the SqlSessionManager creates and destroys the
>> > sessions for you - they are stored either as local variables or on a
>> > ThreadLocal, depending on how you call the methods.
>> >
>> > The code's actually pretty simple - check it out. :)
>> >
>> > Larry
>> >
>> >
>> > On Tue, Jan 25, 2011 at 9:45 PM, Guy Rouillier
>> <[hidden email] <mailto:[hidden email]>> wrote:
>> >> On 1/25/2011 8:03 AM, Larry Meadors wrote:
>> >>>
>> >>> On Mon, Jan 24, 2011 at 8:18 PM, Rick R<[hidden email]
>> <mailto:[hidden email]>> wrote:
>> >>>>
>> >>>> Even cleaner using the SqlSessionMapper:
>> >>>>
>> >>>> public List<Prompt> findPromptsForPromptType(Integer
>> promptTypeID) {
>> >>>> PromptMapper mapper =
>> MetaDbIbatisConfig.getSqlSessionManager()
>> >>>> .getMapper(PromptMapper.class);
>> >>>> return mapper.findPromptsForPromptType(promptTypeID);
>> >>>> }
>> >>>
>> >>> Actually, Rick, you can make that even simpler - the managed
>> mappers
>> >>> don't need to be retrieved every time, so you can make them
>> members on
>> >>> your class, set them them in the constructor and then your
>> method is
>> >>> just this:
>> >>
>> >> I don't understand how that works. A session is a wrapper around a
>> >> Connection. I've always assumed that you get a mapper from a
>> session object
>> >> so that the mapper has access to the wrapped connection. If you
>> store a
>> >> reference to a mapper and then close the session, how does the
>> mapper
>> >> associated itself with a new session (and hence connection) the
>> next time
>> >> you retrieve one?
>> >>
>> >> --
>> >> Guy Rouillier
>> >>
>> >
>>
>>
>>
>>
>> --
>> Rick R
>
>


--
Guy Rouillier
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Larry Meadors
On Sat, Apr 2, 2011 at 10:39 PM, Guy Rouillier <[hidden email]> wrote:
> No replies, so I'll ask again my most important question.

SqlSessionManager.close() just closes a previously startManagedSession().

The magic of SSM is that it automagically opens if needed. You really
can't close it.

Larry
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Guy Rouillier
On 4/3/2011 8:47 AM, Larry Meadors wrote:
> On Sat, Apr 2, 2011 at 10:39 PM, Guy Rouillier<[hidden email]>  wrote:
>> No replies, so I'll ask again my most important question.
>
> SqlSessionManager.close() just closes a previously startManagedSession().
>
> The magic of SSM is that it automagically opens if needed. You really
> can't close it.

Thanks for the clarification, Larry.  I read the source after posting,
and I see that close() only applies to startManagedSession().  Per my
previous comment, I'd vote for changing that to end() rather than
close(), since close() is commonly the counterpart to open().

Thanks for SqlSessionManager, I'm trying that with our newest product
under development.  I used SqlSessionFactory in the last one, and I like
that SqlSessionManager does additional housekeeping for me.

--
Guy Rouillier
Reply | Threaded
Open this post in threaded view
|

Re: Been a while for me - Best practice for 'cleaning up' all this Session get/close clutter?

Clinton Begin
Administrator
Everyone loves SqlSessionManager... kudos to Larry for winning that debate.  :-) 

I know the code above was just sample code, but just in case:  don't forget the try / finally block.

ssm.startManagedSession();
try {
  getContactById();
  getAllContacts();
} finally {
  ssm.close();
}

Cheers,
Clinton

On Sun, Apr 3, 2011 at 7:03 PM, Guy Rouillier <[hidden email]> wrote:
On 4/3/2011 8:47 AM, Larry Meadors wrote:
On Sat, Apr 2, 2011 at 10:39 PM, Guy Rouillier<[hidden email]>  wrote:
No replies, so I'll ask again my most important question.

SqlSessionManager.close() just closes a previously startManagedSession().

The magic of SSM is that it automagically opens if needed. You really
can't close it.

Thanks for the clarification, Larry.  I read the source after posting, and I see that close() only applies to startManagedSession().  Per my previous comment, I'd vote for changing that to end() rather than close(), since close() is commonly the counterpart to open().

Thanks for SqlSessionManager, I'm trying that with our newest product under development.  I used SqlSessionFactory in the last one, and I like that SqlSessionManager does additional housekeeping for me.

--
Guy Rouillier

12