MyBatis直接执行SQL的工具SqlMapper

可能有些人也有过相似需求,通常都会选择使用其余的方式如Spring-JDBC等方式解决。java

可否经过MyBatis实现这样的功能呢?git

为了让通用Mapper更完全的支持多表操做以及更灵活的操做,在<b>2.2.0版本</b>增长了一个能够直接执行SQL的新类SqlMappergithub

注:3.3.0版本去掉了这个类,这个类如今在EntityMapper项目sql

经过这篇博客,咱们来了解一下SqlMapper缓存

##SqlMapper提供的方法app

SqlMapper提供了如下这些公共方法:测试

  • Map<String,Object> selectOne(String sql)ui

  • Map<String,Object> selectOne(String sql, Object value).net

  • <T> T selectOne(String sql, Class<T> resultType)prototype

  • <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还支持下面这种复杂形式:

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,在通常系统中,能够按照下面的方式获取:

SqlSession sqlSession = (...);//经过某些方法获取sqlSession
//建立sqlMapper
SqlMapper sqlMapper = new SqlMapper(sqlSession);

若是使用的Spring,那么能够按照下面的方式配置<bean>:

<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

//查询,返回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 &lt; #{id}" +
        "       </if>" +
        "   </where>" +
        "</script>", country, Country.class);

###selectOne

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

//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参数

##实现原理

2015-03-09:最初想要设计这个功能的时候,感受会很复杂,想的也复杂,须要不少个类,所以当时没有实现。

2015-03-10:突发奇想,设计了如今的这种方式。而且有种强烈的感受就是幸亏昨天没有尝试去实现,由于昨天晚上思考这个问题的时候是晚上10点多,而今天(10号)是晚上7点开始思考。我很庆幸在一个更清醒的状态下去写这段代码。

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

在写MyBatis分页插件的时候熟悉了MappedStatement类。

在写通用Mapper的时候熟悉了xmlSqlNode结构。

若是我根据SQL动态的建立一个MappedStatement,而后使用MappedStatementidsqlSession中执行不就能够了吗?

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

看看下面select查询建立MappedStatement的代码:

/**
 * 建立一个查询的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,不须要参数的,能够直接执行的:

StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);

其中configurationsqlSession中获取,sql就是用户传入到sql语句,是否是也很简单?

另外一种是支持动态sql的,支持参数的SqlSource

SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);

是否是也很简单?这个方法其实能够兼容上面的StaticSqlSource,这里比上面多了一个parameterType,由于这儿是能够传递参数的,另外languageDriver是从configuration中获取的。

是否是很简单?

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

insert,delete,update方法的建立更容易,由于他们的返回值都是int,因此处理起来更简单,有兴趣的能够去通用Mapper下的包com.github.abel533.sql中查看SqlMapper的源码。

相关文章
相关标签/搜索