mybatis-guice transaction across two datasources

223 views
Skip to first unread message

Ilko

unread,
Oct 6, 2015, 11:26:38 AM10/6/15
to mybatis-user
Hi,

I've searched and tried a lot, but I couldn't get this to work - is it actually possible:

In my configuration I have two data sources configured in privatemodules as stated in the docs. My mappers are bound and exposed in these private modules as well. I have my services bound and exposed in the modules. For example:

MyModule1 extends PrivateModule {

@Override
    protected void configure() {
        install(new XMLMyBatisModule() {

            @Override
            protected void initialize() {
                setEnvironmentId(getEnvironmentId());
                setClassPathResource(MYBATIS_CONFIG_FILE_DS1);

                bind(ServiceDS1.class).to(ServiceDS1Impl.class);
                bind(ServiceDS1Impl.class); // workaoruind for guice 722
          }
});

bind(SqlSession.class).annotatedWith(SqlSessionDS1.class).toProvider(SqlSessionManagerProvider.class).in(
                Scopes.SINGLETON);
        expose(SqlSession.class).annotatedWith(SqlSessionDS1.class);

expose(Mapper1DS1.class);
...
expose(ServiceDS1.class);
 }
}

MyModule2 is similar to MyModule1

The Injector is created with these two modules and transactions are rollbacked, if they are within a single session. But how can I implement a service class with a single transaction across the two datasources? If I scope this new service in one of the private modules only the statements applicable to this session will be managed in a transaction and rollbacked on exception accordingly. If I put this service in a new normal module then it won't be intercepted even if its method is annotated with transactional ending again in transaction pro nested method.

 

Jeff Butler

unread,
Oct 6, 2015, 5:07:11 PM10/6/15
to mybati...@googlegroups.com
I've never tried it, but here are a couple of thoughts.

1. MyBatis does not have code to manage transactions across databases.  So you need to use an external transaction manager that supports 2-phase commit.  Without that, the rest of this is useless.

2. Assuming you have that, does it work if you create a third service class and then inject the two existing services into that class?  Something like this:

public class SuperService {

  @Inject
  private ServiceDS1 service1;
  @Inject
  private ServiceDS2 service2;

  @Transactional
  public void doSomething() {
    service1.doSomething();
    service2.doSomething();
  }
}

Jeff Butler


--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ilko

unread,
Oct 7, 2015, 3:41:55 AM10/7/15
to mybatis-user
Hi Jeff,

Thanks for the answer. The problem I have is where should I define SuperService. The only reasonable place I can think of is an independent module, otherwise it is bound to the corresponding sqlsession in the private module. So, I've tried with a new AbstractModule, which is used in the creation of the Injector with the other two private modules. The problem I have with this approach is that the SuperService's method is not intercepted, only the nested methods and I end with tx pro nested method. I'm not sure how it is supposed to work. On the other side I saw the XATransaction interceptors in the package, but I still can't find out how they should be used - perhaps that is the missing part of the puzzle. If someone can provide an example here it would be very helpful.

TIA,
Ilko

Poitras Christian

unread,
Oct 7, 2015, 9:46:24 AM10/7/15
to mybati...@googlegroups.com
Hi,

Normally, MyBatis-Guice should intercept methods that are defined in an
independent module.
See
http://stackoverflow.com/questions/16788024/mybatis-guice-3-3-multiple-data
sources-properties-scriptrunner


MyBatis-Guice has a very simple way of dealing with multiple data sources,
so using XATransaction is probably better.
See the XATransaction issue for some examples.
https://github.com/mybatis/guice/issues/29


The doc for JTA transaction should be on the MyBatis-Guice web site, but
due to problems during the release process, the doc was not updated.


Christian


Le 2015-10-07, 3:41 AM, « mybati...@googlegroups.com on behalf of Ilko
» <mybati...@googlegroups.com on behalf of ilkom...@gmail.com> a
écrit :

Ilko

unread,
Oct 7, 2015, 11:01:22 AM10/7/15
to mybatis-user
ok, I see now that I have go bind the DbADao and DbBDao in the services module as well. I have to try it but it sounds logical. Thanks for the samples!

Jeff Butler

unread,
Oct 7, 2015, 1:01:13 PM10/7/15
to mybati...@googlegroups.com
I coded up a little project just to see how this works.  The new MyBatisJtaModule works very nicely.

However I found that the interceptor will fail if a @Transactional method calls another @Transactional method.  It tries to start a transaction in both cases and there is already one started.

@Christian - do you know if this was intentionalal or just an oversight?  The non-JTA interceptor doesn't have this issue.

Jeff Butler

Poitras Christian

unread,
Oct 7, 2015, 1:13:12 PM10/7/15
to mybati...@googlegroups.com
Hi Jeff,

That is most likely an error.
Can you file an issue for this, please?

De : <mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>> on behalf of Jeff Butler <jeffg...@gmail.com<mailto:jeffg...@gmail.com>>
Répondre à : "mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>" <mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>>
Date : Wednesday, October 7, 2015 at 1:01 PM
À : "mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>" <mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>>
Objet : Re: mybatis-guice transaction across two datasources

I coded up a little project just to see how this works. The new MyBatisJtaModule works very nicely.

However I found that the interceptor will fail if a @Transactional method calls another @Transactional method. It tries to start a transaction in both cases and there is already one started.

@Christian - do you know if this was intentionalal or just an oversight? The non-JTA interceptor doesn't have this issue.

Jeff Butler


On Wed, Oct 7, 2015 at 11:01 AM, Ilko <ilkom...@gmail.com<mailto:ilkom...@gmail.com>> wrote:
ok, I see now that I have go bind the DbADao and DbBDao in the services module as well. I have to try it but it sounds logical. Thanks for the samples!

On Wednesday, October 7, 2015 at 3:46:24 PM UTC+2, christia...@ircm.qc.ca<mailto:christia...@ircm.qc.ca> wrote:
Hi,

Normally, MyBatis-Guice should intercept methods that are defined in an
independent module.
See
http://stackoverflow.com/questions/16788024/mybatis-guice-3-3-multiple-data
sources-properties-scriptrunner<http://stackoverflow.com/questions/16788024/mybatis-guice-3-3-multiple-datasources-properties-scriptrunner>
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user...@googlegroups.com>.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user...@googlegroups.com>.

Ilko

unread,
Oct 9, 2015, 9:45:34 AM10/9/15
to mybatis-user
Hi Jeff, Christian

sorry but I can't get it to work at all - my method is not intercepted. Jeff, perhaps you can share your simple project if it is possible? Here details from my tests, slightly changed not to reflect the real db tables / columns - may be someone can spot an obvious error, which I'm overlooking:

SuperService:

public class SuperServiceImpl implements SuperService {

    @Inject
    private DaoDS1 daoDS1;

    @Inject
    private DaoDS2 daoDS2;

    @Transactional
    public void updateTwoDatasources(Integer id1, Long id2) {
        FromDS1 fromDS1 = daoDS1.selectById(id1);
        fromDS1.setYYY("test");
        daoDS1.update(fromDS1);
        FromDS2 fromDS2 = daoDS2.selectById(id2);
        fromDS2.setAAA("ddd");
        daoDS2.update(fromDS2);
        throw new RuntimeException("rollback me!");
    }
}

DaoImpl:

public class DaoDS1Impl implements DaoDS1 {

    @Inject
    @Named("DS1")
    private SqlSession sqlSession;

    public int insert(XXX xxx) {
        return sqlSession.getMapper(XXXMapper.class).insert(xxx);
    }
   
    ... similar implementation for the other CRUD methods
   
}


Injector creation:

private static void createInjector() {
        injector = Guice.createInjector(new PrivateModule() {

            @Override
            protected void configure() {
                install(new XMLMyBatisModule() {
                    @Override
                    protected void initialize() {
                        setEnvironmentId("dev");
                        setClassPathResource("mybatis-config-ds1.xml");
                    }
                });

                bind(SqlSession.class).annotatedWith(Names.named("DS1")).toProvider(SqlSessionManagerProvider.class).in(Scopes.SINGLETON);
                expose(SqlSession.class).annotatedWith(Names.named("DS1"));
            }
        }, new PrivateModule() {

            @Override
            protected void configure() {
                install(new XMLMyBatisModule() {
                    @Override
                    protected void initialize() {
                        setEnvironmentId("dev");
                        setClassPathResource("mybatis-config-ds2.xml");
                    }
                });

                bind(SqlSession.class).annotatedWith(Names.named("DS2")).toProvider(SqlSessionManagerProvider.class).in(Scopes.SINGLETON);
                expose(SqlSession.class).annotatedWith(Names.named("DS2"));
            }
        }, new AbstractModule() {
            @Override
            protected void configure() {
                bind(DaoDS1.class).to(DaoDS1Impl.class).in(Scopes.SINGLETON);
                bind(DaoDS2.class).to(DaoDS2Impl.class).in(Scopes.SINGLETON);
                bind(SuperService.class).to(SuperServiceImpl.class).in(Scopes.SINGLETON);
            }
        });
    }

the unit test:

public class SuperServiceTest {

    @Test
    public void testSuperService() throws Exception {
        SuperService superService = getService(SuperService.class);
        System.out.format("SuperService guice: %s%n", superService);
        Integer id1 = 459727;
        Long id2 = 2655587L;
        superService.updateTwoDatasources(id1, id2);
    }

    private static <T> T getService(Class<T> serviceClass) {
        T instance = BootClass.getInjector().getInstance(serviceClass);
        return instance;
    }

}
   
logger output:

SuperService guice: ilko.tests.mybatis.guice.services.SuperServiceImpl@7c9d8e2   <---- not enhanced!   
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 1000966072.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - ==>  Preparing: select xxx
DEBUG [main] - ==> Parameters: xxx
TRACE [main] - <==    Columns: xxx
TRACE [main] - <==        Row: xxx
DEBUG [main] - <==      Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - Closing JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - Returned connection 1000966072 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 1000966072 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - ==>  Preparing: update xxx
DEBUG [main] - ==> Parameters: xxx
DEBUG [main] - <==    Updates: 1
DEBUG [main] - Committing JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - Closing JDBC Connection [com.ibm.db2.jcc.t4.b@3ba987b8]
DEBUG [main] - Returned connection 1000966072 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 51554940.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.ibm.db2.jcc.t4.b@312aa7c]
DEBUG [main] - ==>  Preparing: select xxx
DEBUG [main] - ==> Parameters: xxx
...
similar for the other statements - each time the statement will be commited
...
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.ibm.db2.jcc.t4.b@312aa7c]
DEBUG [main] - Closing JDBC Connection [com.ibm.db2.jcc.t4.b@312aa7c]
DEBUG [main] - Returned connection 51554940 to pool.
...

Poitras Christian

unread,
Oct 9, 2015, 10:00:51 AM10/9/15
to mybati...@googlegroups.com
I don’t know if it would solve your issue, but I always inject mappers directly in the Service/DAO.
I never inject SqlSession unless I am doing a nasty hack that I cannot do with mappers.

De : <mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>> on behalf of Ilko <ilkom...@gmail.com<mailto:ilkom...@gmail.com>>
Date : Friday, October 9, 2015 at 9:45 AM
À : mybatis-user <mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>>
Objet : Re: mybatis-guice transaction across two datasources

On Wed, Oct 7, 2015 at 11:01 AM, Ilko <ilkom...@gmail.com<javascript:>> wrote:
ok, I see now that I have go bind the DbADao and DbBDao in the services module as well. I have to try it but it sounds logical. Thanks for the samples!

On Wednesday, October 7, 2015 at 3:46:24 PM UTC+2, christia...@ircm.qc.ca wrote:
Hi,

Normally, MyBatis-Guice should intercept methods that are defined in an
independent module.
See
http://stackoverflow.com/questions/16788024/mybatis-guice-3-3-multiple-data
sources-properties-scriptrunner<http://stackoverflow.com/questions/16788024/mybatis-guice-3-3-multiple-datasources-properties-scriptrunner>
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<javascript:>.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user...@googlegroups.com>.

Jeff Butler

unread,
Oct 9, 2015, 10:11:51 AM10/9/15
to mybati...@googlegroups.com
I also noticed you are not using the JTA module.

I'll add my test to git a bit later today.  I think it's less abstract and a bit easier to understand than the existing test code, plus it will be the basis for fixing the issue I found.

One question - are you running this in some kind of container that has a transaction manager, or is it totally standalone?

Jeff Butler

Ilko

unread,
Oct 9, 2015, 10:41:00 AM10/9/15
to mybatis-user
Hi Jeff,

thanks for the quick reply. At the moment it is a simple standalone junit test, because I need to get the injection part figured out - I think this should always work?

At the end this will be deployed in appserver (WebSphere), but I try to find a solution to use it in standalone mode as well in order to be able to use the same components in batch programs.

I'm converting my project to real sample project independent of corporate code and db here and I'll post it on github as well - I think of derby as database, because it is the closest open source match to the db2 in use here.

as soon as it is ready I'll share the link here.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user+unsubscribe@googlegroups.com>.

Jeff Butler

unread,
Oct 9, 2015, 3:36:20 PM10/9/15
to mybati...@googlegroups.com
I've committed my sample here:


It matches a configuration that you might use in a JEE container if you were not using container managed transactions.  If you do plan to use container managed transactions, then you don't need any of this - the normal ExternalTransaction manager will work just fine in that case.

By far the most difficult thing in making something like this work in a unit test is that you need to mimic the functions of a typical container provided transaction manager.  Everything else is very straight forward.

@Christian - I closed the issue I created because it works as expected in this unit test.  Must have been a WebLogic configuration problem on my part.

Jeff Butler


To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user...@googlegroups.com>.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ilko

unread,
Oct 11, 2015, 8:55:58 AM10/11/15
to mybatis-user
Hi,

@Jeff - I've checked it out and played a bit with it. You are exposing the CombinedService in the PrivateModule and that's why it is guice-enhanced. On the other side the 2PC manager works here as expected and rollbacks accordingly, thus it does not make implementation difference where the service definition is done. However, if I put it in its own Module outside the two private modules the service class is not guice enhanced and the tests are failing. For example the output from your tests unchanged:

CombinedService org.mybatis.guice.jta.simple.CombinedService$$EnhancerByGuice$$7f5b01e0@6f46426d

However, if I configure it in its own module like this:

, new AbstractModule() {
@Override
protected void configure() {
                bind(CombinedService.class);
}
});

I get a normal object without proxy.

@Christian - according to your reply this should actually work? Regarding the SqlSession injection - I've tried here to reproduce most closely the sample in your link (GWT use in the stackoverflow link from you) - in the real project I inject the mappers directly as well, but it is done in a small inheritance implementation with generics (I've submitted a pull request to mybatis3 project in order to make this work [1], so it is not possible to be tested publicly with the latest mybatis release version) - I wanted to test the case with the @Transactional interception as basic as I can to avoid other possible errors.

So, my initial problem remains still open - I can't get @Transactional interception if I define my services in an independent module. As far as I can see the multidstest package doesn't cover this use case - it just tests the two PrivateModules on their own. I've added simple test case based on Jeff's simple jta, but without the overhead of the tx manager - it could be found in this commit [2].

Is this an issue or expected behaviour?



On Friday, October 9, 2015 at 9:36:20 PM UTC+2, Jeff Butler wrote:
I've committed my sample here:


It matches a configuration that you might use in a JEE container if you were not using container managed transactions.  If you do plan to use container managed transactions, then you don't need any of this - the normal ExternalTransaction manager will work just fine in that case.

By far the most difficult thing in making something like this work in a unit test is that you need to mimic the functions of a typical container provided transaction manager.  Everything else is very straight forward.

@Christian - I closed the issue I created because it works as expected in this unit test.  Must have been a WebLogic configuration problem on my part.

Jeff Butler

On Fri, Oct 9, 2015 at 10:41 AM, Ilko <ilkom...@gmail.com> wrote:
Hi Jeff,

thanks for the quick reply. At the moment it is a simple standalone junit test, because I need to get the injection part figured out - I think this should always work?

At the end this will be deployed in appserver (WebSphere), but I try to find a solution to use it in standalone mode as well in order to be able to use the same components in batch programs.

I'm converting my project to real sample project independent of corporate code and db here and I'll post it on github as well - I think of derby as database, because it is the closest open source match to the db2 in use here.

as soon as it is ready I'll share the link here.



On Friday, October 9, 2015 at 4:11:51 PM UTC+2, Jeff Butler wrote:
I also noticed you are not using the JTA module.

I'll add my test to git a bit later today.  I think it's less abstract and a bit easier to understand than the existing test code, plus it will be the basis for fixing the issue I found.

One question - are you running this in some kind of container that has a transaction manager, or is it totally standalone?

Jeff Butler

On Fri, Oct 9, 2015 at 10:00 AM, Poitras Christian <Christia...@ircm.qc.ca> wrote:
I don’t know if it would solve your issue, but I always inject mappers directly in the Service/DAO.
I never inject SqlSession unless I am doing a nasty hack that I cannot do with mappers.

To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user+unsub...@googlegroups.com>.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jeff Butler

unread,
Oct 11, 2015, 1:31:49 PM10/11/15
to mybati...@googlegroups.com
Well that is the Guice #722 issue/feature.  So I guess I would say this is expected behavior.

It makes sense if you think about it.  @Transactional is a MyBatis thing - not a Guice thing.  A generic Guice module would have no idea what @Transactional means.  With private modules, the scope of the MyBatis transactional interceptor is limited to things in the private module - not the entire Guice container.

Jeff Butler


To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user...@googlegroups.com>.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ilko

unread,
Oct 12, 2015, 2:24:44 PM10/12/15
to mybatis-user
ok, I think that we have conflicting requirements here caused by the Guice DI - on one side the private modules are necessary to hide the different datasources from each other on the other side these must be global available to be used in a single transaction. It seems that without any architectural changes this can't be accomplished. I wonder only, if the example from stackoverflow as mentioned by Christian with the GWT platform really works and if so, what kind of magic it is doing behind the scene...
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com<mailto:mybatis-user+unsub...@googlegroups.com>.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "mybatis-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mybatis-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Poitras Christian

unread,
Oct 13, 2015, 9:38:14 AM10/13/15
to mybati...@googlegroups.com
Hi,

Jeff is right. I thought Guice would be able to intercept the method, but it cannot due to the limited scope of the private module.

It means all services must be bound in the private MyBatisModule.
If you use 2 databases, then services using a specific database must in bound in the module defining this database.
Combined services can be declared in any MyBatisModule as long as they only use specific database services defined in a private module for database access.

Sorry for my mistake.
Christian
Date : Sunday, October 11, 2015 at 1:31 PM
À : "mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>" <mybati...@googlegroups.com<mailto:mybati...@googlegroups.com>>
Objet : Re: mybatis-guice transaction across two datasources

Well that is the Guice #722 issue/feature. So I guess I would say this is expected behavior.

It makes sense if you think about it. @Transactional is a MyBatis thing - not a Guice thing. A generic Guice module would have no idea what @Transactional means. With private modules, the scope of the MyBatis transactional interceptor is limited to things in the private module - not the entire Guice container.

Jeff Butler


Poitras Christian

unread,
Oct 13, 2015, 10:04:56 AM10/13/15
to mybati...@googlegroups.com
I¹ll update the examples in MyBatis-Guice doc since they are misleading.
They probably work only because of the just in time binding.

Christian

Le 2015-10-13, 9:38 AM, « mybati...@googlegroups.com on behalf of
Poitras Christian » <mybati...@googlegroups.com on behalf of
Christia...@ircm.qc.ca> a écrit :
>mybatis-user...@googlegroups.com<mailto:mybatis-user+unsubscribe@googlegro
>ups.com>.
>For more options, visit https://groups.google.com/d/optout.
>
>--
>You received this message because you are subscribed to the Google Groups
>"mybatis-user" group.
>To unsubscribe from this group and stop receiving emails from it, send an
>email to mybatis-user...@googlegroups.com.
>For more options, visit https://groups.google.com/d/optout.
>
>
>--
>You received this message because you are subscribed to the Google Groups
>"mybatis-user" group.
>To unsubscribe from this group and stop receiving emails from it, send an
>email to mybatis-user...@googlegroups.com.
>For more options, visit https://groups.google.com/d/optout.
>
>
>--
>You received this message because you are subscribed to the Google Groups
>"mybatis-user" group.
>To unsubscribe from this group and stop receiving emails from it, send an
>email to
>mybatis-user...@googlegroups.com<mailto:mybatis-user+unsubscribe@
>googlegroups.com>.
>For more options, visit https://groups.google.com/d/optout.
>
>
>--
>You received this message because you are subscribed to the Google Groups
>"mybatis-user" group.
>To unsubscribe from this group and stop receiving emails from it, send an
>email to
>mybatis-user...@googlegroups.com<mailto:mybatis-user+unsubscribe@
Reply all
Reply to author
Forward
0 new messages