好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MyBatis直接执行SQL的工具SqlMapper

可能有些人也有过类似需求,一般都会选择使用其他的方式如spring-jdbc等方式解决。

能否通过mybatis实现这样的功能呢?

为了让通用mapper更彻底的支持多表操作以及更灵活的操作,在2.2.0版本增加了一个可以直接执行sql的新类sqlmapper。

我们来了解一下sqlmapper。

sqlmapper提供的方法

sqlmapper提供了以下这些公共方法:

map<string,object> selectone(string sql) map<string,object> selectone(string sql, object value) <t> t selectone(string sql, class<t> resulttype) <t> t selectone(string sql, object value, class<t> resulttype) list<map<string,object>> selectlist(string sql) list<map<string,object>> selectlist(string sql, object value) <t> list<t> selectlist(string sql, class<t> resulttype) <t> list<t> selectlist(string sql, object value, class<t> resulttype) int insert(string sql) int insert(string sql, object value) int update(string sql) int update(string sql, object value) int delete(string sql) int delete(string sql, object value)

一共14个方法,这些方法的命名和参数和sqlsession接口的很像,只是基本上第一个参数都成了sql。

其中object value为入参,入参形式和sqlsession中的入参一样,带有入参的方法,在使用时sql可以包含#{param}或${param}形式的参数,这些参数需要通过入参来传值。需要的参数过多的时候,参数可以使用map类型。另外这种情况下的sql还支持下面这种复杂形式:

?

1

2

string sql = "<script>select * from sys_user where 1=1" +

     "<if test=\"usertype != null\">usertype = #{usertype}</if></script>" ;

这种情况用的比较少,不多说。

不带有object value的所有方法,sql中如果有参数需要手动拼接成一个可以直接执行的sql语句。

在selectxxx方法中,使用class<t> resulttype可以指定返回类型,否则就是map<string,object>类型。

实例化sqlmapper

sqlmapper构造参数public sqlmapper(sqlsession sqlsession),需要一个入参sqlsession sqlsession,在一般系统中,可以按照下面的方式获取:

?

1

2

3

sqlsession sqlsession = (...); //通过某些方法获取sqlsession

//创建sqlmapper

sqlmapper sqlmapper = new sqlmapper(sqlsession);

如果使用的spring,那么可以按照下面的方式配置<bean>:

?

1

2

3

<bean id= "sqlmapper" class = "com.github.abel533.sql.sqlmapper" scope= "prototype" >

  <constructor-arg ref= "sqlsession" />

</bean>

在service中使用的时候可以直接使用@autowired注入。

简单例子

在src/test/java目录的com.github.abel533.sql包中包含这些方法的测试。

下面挑几个看看如何使用。

selectlist

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//查询,返回list<map>

list<map<string, object>> list = sqlmapper.selectlist( "select * from country where id < 11" );

//查询,返回指定的实体类

list<country> countrylist = sqlmapper.selectlist( "select * from country where id < 11" , country. class );

//查询,带参数

countrylist = sqlmapper.selectlist( "select * from country where id < #{id}" , 11 , country. class );

//复杂点的查询,这里参数和上面不同的地方,在于传入了一个对象

country country = new country();

country.setid( 11 );

countrylist = sqlmapper.selectlist( "<script>" +

     "select * from country " +

     "  <where>" +

     "    <if test=\"id != null\">" +

     "      id < #{id}" +

     "    </if>" +

     "  </where>" +

     "</script>" , country, country. class );

selectone

?

1

2

3

4

map<string, object> map = sqlmapper.selectone( "select * from country where id = 35" );

map = sqlmapper.selectone( "select * from country where id = #{id}" , 35 );

country country = sqlmapper.selectone( "select * from country where id = 35" , country. class );

country = sqlmapper.selectone( "select * from country where id = #{id}" , 35 , country. class );

insert,update,delete

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//insert

int result = sqlmapper.insert( "insert into country values(1921,'天朝','tc')" );

country tc = new country();

tc.setid( 1921 );

tc.setcountryname( "天朝" );

tc.setcountrycode( "tc" );

//注意这里的countrycode和countryname故意写反的

result = sqlmapper.insert( "insert into country values(#{id},#{countrycode},#{countryname})"

              , tc);

//update

result = sqlmapper.update( "update country set countryname = '天朝' where id = 35" );

tc = new country();

tc.setid( 35 );

tc.setcountryname( "天朝" );

int result = sqlmapper.update( "update country set countryname = #{countryname}" +

       " where id in(select id from country where countryname like 'a%')" , tc);

//delete

result = sqlmapper.delete( "delete from country where id = 35" );

result = sqlmapper.delete( "delete from country where id = #{id}" , 35 );

注意

通过上面这些例子应该能对此有个基本的了解,但是如果你使用参数方式,建议阅读下面的文章:

深入了解mybatis参数

实现原理

最初想要设计这个功能的时候,感觉会很复杂,想的也复杂,需要很多个类,因此当时没有实现。

突发奇想,设计了现在的这种方式。并且有种强烈的感觉就是幸好昨天没有尝试去实现,因为昨天晚上思考这个问题的时候是晚上10点多,而今天晚上7点开始思考。我很庆幸在一个更清醒的状态下去写这段代码。

下面简单说思路和实现方式。

在写mybatis分页插件的时候熟悉了mappedstatement类。

在写通用mapper的时候熟悉了xml转sqlnode结构。

如果我根据sql动态的创建一个mappedstatement,然后使用mappedstatement的id在sqlsession中执行不就可以了吗?

想到这一点,一切就简单了。

看看下面select查询创建mappedstatement的代码:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

/**

  * 创建一个查询的ms

  * @param msid

  * @param sqlsource 执行的sqlsource

  * @param resulttype 返回的结果类型

  */

private void newselectmappedstatement(string msid, sqlsource sqlsource, final class <?> resulttype) {

   mappedstatement ms = new mappedstatement.builder(

       configuration, msid, sqlsource, sqlcommandtype.select)

     .resultmaps( new arraylist<resultmap>() {

       {

         add( new resultmap.builder(configuration,

             "defaultresultmap" ,

             resulttype,

             new arraylist<resultmapping>( 0 )).build());

       }

     })

     .build();

   //缓存

   configuration.addmappedstatement(ms);

}

代码是不是很简单,这段代码的关键是参数sqlsource,下面是创建sqlsource的方法,分为两种。

一种是一个完整的sql,不需要参数的,可以直接执行的:

?

1

staticsqlsource sqlsource = new staticsqlsource(configuration, sql);

其中configuration从sqlsession中获取,sql就是用户传入到sql语句,是不是也很简单?

另一种是支持动态sql的,支持参数的sqlsource:

?

1

sqlsource sqlsource = languagedriver.createsqlsource(configuration, sql, parametertype);

是不是也很简单?这个方法其实可以兼容上面的staticsqlsource,这里比上面多了一个parametertype,因为这儿是可以传递参数的,另外languagedriver是从configuration中获取的。

是不是很简单?

我一开始也没想到mybatis直接执行sql实现起来会这么的容易。

insert,delete,update方法的创建更容易,因为他们的返回值都是int,所以处理起来更简单,有兴趣的可以查看sqlmapper的源码。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。如果你想了解更多相关内容请查看下面相关链接

原文链接:https://blog.csdn.net/isea533/article/details/44193939

查看更多关于MyBatis直接执行SQL的工具SqlMapper的详细内容...

  阅读:13次