最近想在自己的项目里实现db sharding功能,正好前段时间研究过ibatis的源码于是就在ibatis的基础上进行了一些修改。另一方面也是为了练练手。这个sharding的实现主要是基于我项目中的需求实现的可能有很多考虑不周的地方,希望各位大牛拍砖。如果有人感兴趣愿意一起来发展这个项目,本人也非常欢迎各位的加入。
shardbatis是在mybatis 2.3.5代码的基础上进行一些扩展实现数据水平切分功能。 数据的水平切分包括多数据库的切分和多表的数据切分。目前shardbatis已经实现了单数据库的数据多表水平切分
mybatis2.3.5的核心类图(包含了spring对ibatis的封装sqlmapclienttemplate)如下(其他版本的ibatis的类图有略微不同)
[img]http://seanhe.iteye.com/upload/picture/pic/70668/cabbe8b9-c6cd-39a5-9ec8-a5158a773281.png[/img]
改造后的类图
[img]http://seanhe.iteye.com/upload/picture/pic/70670/0ca68226-cad5-3db3-b2e1-1deb1647ee73.png[/img]
从这两张图上可以看出shardbatis里新增了接口sqlmapshardingext,sqlmapshardingext中具体的方法如下
通过sqlmapclientimpl和sqlmapsessionimpl对sqlmapshardingext的实现是的ibatis具有了db sharding的功能,ibatis会自动根据配置的或者是编码的sharding策略将原始的sql语句转变为对应目标表名的sql。
下面看一下如何在ibatis中配置和使用sharding功能
1.配置sharding策略。在sql-map-config.xml中添加如下配置
这里再通过实例简单介绍一下使用defaultshardingstrategy时对sql的convert结果
比如sqlmap中定义的原始sql为:
那经过convert后的结果将可能是
又例如原始sql为
convert的结果可能为
开始使用sharding api
实现自己的sharding策略,只要实现一个简单的接口即可
[b]since 0.9.1使用ibatis原生api也可以支持sharding功能[/b]
下面开始编码
最终执行的sql可能是如下样式
关于shardbatis在spring中的使用方法,以及一些使用注意事项和性能测试结果请大家移步到项目主页[url=http://code.google.com/p/shardbatis/]http://code.google.com/p/shardbatis/[/url]上查看
shardbatis是在mybatis 2.3.5代码的基础上进行一些扩展实现数据水平切分功能。 数据的水平切分包括多数据库的切分和多表的数据切分。目前shardbatis已经实现了单数据库的数据多表水平切分
mybatis2.3.5的核心类图(包含了spring对ibatis的封装sqlmapclienttemplate)如下(其他版本的ibatis的类图有略微不同)
[img]http://seanhe.iteye.com/upload/picture/pic/70668/cabbe8b9-c6cd-39a5-9ec8-a5158a773281.png[/img]
改造后的类图
[img]http://seanhe.iteye.com/upload/picture/pic/70670/0ca68226-cad5-3db3-b2e1-1deb1647ee73.png[/img]
从这两张图上可以看出shardbatis里新增了接口sqlmapshardingext,sqlmapshardingext中具体的方法如下
public interface sqlmapshardingext extends sqlmapexecutor{ /** * 带有sharding功能的insert * @param id * @param parameterobject * @param groups * @return * @throws sqlexception */ object insertwithsharding(string id, object parameterobject, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的insert * @param id * @param groups * @return * @throws sqlexception */ object insertwithsharding(string id, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的update * @param id * @param parameterobject * @param groups * @return * @throws sqlexception */ int updatewithsharding(string id, object parameterobject, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的update * @param id * @param groups * @return * @throws sqlexception */ int updatewithsharding(string id, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的delete * @param id * @param parameterobject * @param groups * @return * @throws sqlexception */ int deletewithsharding(string id, object parameterobject, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的delete * @param id * @param groups * @return * @throws sqlexception */ int deletewithsharding(string id, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的单记录查询 * @param id * @param parameterobject * @param groups * @return * @throws sqlexception */ object queryforobjectwithsharding(string id, object parameterobject, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的单记录查询 * @param id * @param groups * @return * @throws sqlexception */ object queryforobjectwithsharding(string id, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的单记录查询 * @param id * @param parameterobject * @param resultobject * @param groups * @return * @throws sqlexception */ object queryforobjectwithsharding(string id, object parameterobject, object resultobject, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的查询 * @param id * @param parameterobject * @param groups * @return * @throws sqlexception */ list queryforlistwithsharding(string id, object parameterobject, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的查询 * @param id * @param groups * @return * @throws sqlexception */ list queryforlistwithsharding(string id, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的查询 * @param id * @param parameterobject * @param skip * @param max * @param groups * @return * @throws sqlexception */ list queryforlistwithsharding(string id, object parameterobject, int skip, int max, shardingfactorgroup... groups) throws sqlexception; /** * 带有sharding功能的查询 * @param id * @param skip * @param max * @param groups * @return * @throws sqlexception */ list queryforlistwithsharding(string id, int skip, int max, shardingfactorgroup... groups) throws sqlexception; /** * * @param id * @param parameterobject * @param rowhandler * @param groups * @throws sqlexception */ void querywithrowhandlerwithsharding(string id, object parameterobject, rowhandler rowhandler, shardingfactorgroup... groups) throws sqlexception; /** * * @param id * @param rowhandler * @param groups * @throws sqlexception */ void querywithrowhandlerwithsharding(string id, rowhandler rowhandler, shardingfactorgroup... groups) throws sqlexception; /** * * @param id * @param parameterobject * @param keyprop * @param groups * @return * @throws sqlexception */ map queryformapwithsharding(string id, object parameterobject, string keyprop, shardingfactorgroup... groups) throws sqlexception; /** * * @param id * @param parameterobject * @param keyprop * @param valueprop * @param groups * @return * @throws sqlexception */ map queryformapwithsharding(string id, object parameterobject, string keyprop, string valueprop, shardingfactorgroup... groups) throws sqlexception;
通过sqlmapclientimpl和sqlmapsessionimpl对sqlmapshardingext的实现是的ibatis具有了db sharding的功能,ibatis会自动根据配置的或者是编码的sharding策略将原始的sql语句转变为对应目标表名的sql。
下面看一下如何在ibatis中配置和使用sharding功能
1.配置sharding策略。在sql-map-config.xml中添加如下配置
<shardingconfig> <!-- 对于app_test使用默认的切分策略,默认的切分策略只是简单按数值取模生成新的表名,如何实现切分策略后面再介绍 --> <sharding tablename="app_test" strategyclass="com.ibatis.sqlmap.engine.sharding.impl.defaultshardingstrategy"/> <!-- 没有配置切分策略等于使用默认的切分策略 --> <sharding tablename="app_2test" /></shardingconfig>
这里再通过实例简单介绍一下使用defaultshardingstrategy时对sql的convert结果
比如sqlmap中定义的原始sql为:
select employeeidno from mytable where salary >= 50000
那经过convert后的结果将可能是
select employeeidno from mytable_1 where salary >= 50000
又例如原始sql为
select a.* from antiques a,antiqueowners b, mytable c where a.id=b.id and b.id=c.id
convert的结果可能为
select a.* from antiques_0 as a, antiqueowners_1 as b, mytable_1 as c where a.id = b.id and b.id = c.id
开始使用sharding api
public class sqlmapclienttest { sqlmapclient sqlmapper; @before public void init() { reader reader; try { reader = resources.getresourceasreader("sql-map-config.xml"); sqlmapper = sqlmapclientbuilder.buildsqlmapclient(reader); reader.close(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } @test public void testsharding() throws exception { map param = new hashmap(); param.put("cnt", "ttt"); shardingfactorgroup g = new shardingfactorgroup();//shardingfactorgroup为切分策略提供必要参数 g.settablename("app_test");//设置为哪张表配置切分策略 g.setparam(new integer(123));//设置为切分策略提供参数 //这里还可以通过g.setshardingstrategy(...)来设置切分策略 //通过api配置的切分策略可以覆盖xml里配置的app_test表的切分策略 integer count = (integer) sqlmapper.queryforobjectwithsharding( "apptest.select_paging_count_by_map", param, g); assert.assertequals(count.tostring(), "0"); } @test public void testupdate() throws sqlexception { shardingfactorgroup g = new shardingfactorgroup(); g.settablename("app_test"); g.setparam(new integer(123)); string cnt = "testupdate" + system.currenttimemillis(); apptest at1 = new apptest(); at1.setcnt(cnt); integer id = (integer) sqlmapper.insertwithsharding( "apptest.insert_h2", at1, g); apptest parameterobject = new apptest(); parameterobject.setcnt(cnt); apptest ret = (apptest) sqlmapper.queryforobjectwithsharding( "apptest.select_by_condition", parameterobject, g); assert.assertequals(ret.getid().tostring(), id.tostring()); ret.setcnt("new_content"); integer count = sqlmapper.updatewithsharding("apptest.update", ret, g); assert.assertequals(count.tostring(), "1"); count = (integer) sqlmapper.queryforobjectwithsharding( "apptest.select_paging_count", ret, g); assert.assertequals(count.tostring(), "1"); } @test public void testdelete() throws sqlexception { shardingfactorgroup g = new shardingfactorgroup(); g.settablename("app_test"); g.setparam(new integer(123)); string cnt = "testdelete" + system.currenttimemillis(); apptest at1 = new apptest(); at1.setcnt(cnt); integer id = (integer) sqlmapper.insertwithsharding( "apptest.insert_h2", at1, g); apptest parameterobject = new apptest(); parameterobject.setcnt(cnt); apptest ret = (apptest) sqlmapper.queryforobjectwithsharding( "apptest.select_by_condition", parameterobject, g); assert.assertequals(ret.getid().tostring(), id.tostring()); integer row = sqlmapper.deletewithsharding("apptest.delete", ret, g); assert.assertequals(row.tostring(), "1"); }}
实现自己的sharding策略,只要实现一个简单的接口即可
public interface shardingstrategy { /** * 计算得到新的表名 * @param basetablename 逻辑表名 * @param params 为sharding逻辑提供必要参数 * @return */ public string gettargettablename(string basetablename,object params);}
[b]since 0.9.1使用ibatis原生api也可以支持sharding功能[/b]
<!-- 在sql-map-config.xml中添加如下配置 --><shardingconfig> <!-- 对于app_test使用默认的切分策略,默认的切分策略只是简单按数值取模生成新的表名,如何实现切分策略后面再介绍 --> <sharding tablename="app_test" strategyclass="com.ibatis.sqlmap.engine.sharding.impl.defaultshardingstrategy"/> <!-- 没有配置切分策略等于使用默认的切分策略 --> <sharding tablename="app_2test" /></shardingconfig><!-- 在sqlmap.xml中statement节点都可以添加shardingparams属性shardingparams对应的值使用json数组格式,paramexpr 对应parameterclass对象中的某个属性,这个属性的值会传递给sharding策略用于计算新的表名,例如paramexpr:testid 等同于apptest.gettestid;paramexpr:model.id 等同于apptest.getmodel().getid()tablename 用于表示当前这个json对象配使用于sql中的哪个表strategyclass strateg接口实现类的全名,这个值如果没有将使用sql-map-config.xml里shardingconfig下配置对应的值。--><select id="select_count_native" parameterclass="apptest" resultclass="java.lang.integer" shardingparams='[{"paramexpr":"testid","tablename":"app_test"}]'> select count(*) from app_test where cnt=#cnt#</select>
下面开始编码
apptest param=new apptest();param.settestid(2);param.setcnt("testshardingwithconfig");integer count=(integer)sqlmapper.queryforobject("apptest.select_count_native",param);//和使用原生的ibatis api没有区别
最终执行的sql可能是如下样式
select count(*) from app_test_0 where cnt=?
关于shardbatis在spring中的使用方法,以及一些使用注意事项和性能测试结果请大家移步到项目主页[url=http://code.google.com/p/shardbatis/]http://code.google.com/p/shardbatis/[/url]上查看