转载

jSqlBox 3.0.0发布, 自带分布式事务的 Java ORM 工具

jSqlBox3.0.0 发布

jSqlBox 是一个建立在 DbUtils 内核上的 Java 全功能数据库持久层工具,具备跨数据库(80 多种方言)、DD L生成、分页、多种SQL写法、分库分表、声明式事务、分布式事务、主从、实体 CURD、实体关联查询、ActiveRecord、Sql 模板等功能。

本次更新主要内容:支持分库分表的分布式事务

Gtx 事务是 jSqlBox3.0.0 新增的分布式事务模块,它的总体思路和 Seata 类似,也是通过生成反向记录来自动回滚,减小对业务的侵入,但 jSqlBox 采用的思路是将分布式事务建立在 ORM 工具之上,不是分析SQL 内容,而是记录实体的插入、删除、修改操作,以生成回滚记录。这样一来,在实现难度上就降低了一个等级,以牺牲 SQL 支持来达到最好的跨数据库兼容性,支持所有数据库。在具体实现上,它通过采用最大保证完成模式结合全局记录锁的方案,架构请参见 Bag 分布式事务对 SAGA 分布式事务的改进 一文。

jSqlBox 3.0.0发布, 自带分布式事务的 Java ORM 工具

以下为一个分布式事务的配置和演示:

public class GtxTest {
	SqlBoxContext[] ctx = new SqlBoxContext[3];

	private static DataSource newTestDataSource() {
		HikariDataSource ds = new HikariDataSource();
		ds.setDriverClassName("org.h2.Driver");
		ds.setJdbcUrl("jdbc:h2:mem:" + new Random().nextLong() // random h2 ds name
				+ ";MODE=MYSQL;DB_CLOSE_DELAY=-1;TRACE_LEVEL_SYSTEM_OUT=0");
		ds.setUsername("sa");
		ds.setPassword("");
		return ds;
	}

	@Before
	public void init() {
		SqlBoxContext lock = new SqlBoxContext(newTestDataSource());
		lock.setName("lock");
		lock.executeDDL(lock.toCreateDDL(GtxId.class));
		lock.executeDDL(lock.toCreateDDL(GtxLock.class));
		lock.executeDDL(lock.toCreateGtxLogDDL(Usr.class));
		GtxConnectionManager lockCM = new GtxConnectionManager(lock);
		for (int i = 0; i < 3; i++) {
			ctx[i] = new SqlBoxContext(newTestDataSource());
			ctx[i].setName("db");
			ctx[i].setDbCode(i);
			ctx[i].setConnectionManager(lockCM);
			ctx[i].setMasters(ctx);
			ctx[i].executeDDL(ctx[i].toCreateDDL(GtxTag.class));
			ctx[i].executeDDL(ctx[i].toCreateDDL(Usr.class));
		}
	}

	public void Div0Test() {
		ctx[0].startTrans();
		try {
			new Usr().insert(ctx[0]);
			new Usr().insert(ctx[1]);
			new Usr().insert(ctx[1]);
			new Usr().insert(ctx[2]);
			System.out.println(1 / 0);//强制出错
			ctx[0].commitTrans();
		} catch (Exception e) {
			TxResult result=ctx[0].rollbackTrans();
			GtxUnlockServ.forceUnlock(ctx[0], result);
		}
		Assert.assertEquals(0, ctx[0].eCountAll(Usr.class));
		Assert.assertEquals(0, ctx[1].eCountAll(Usr.class));
		Assert.assertEquals(0, ctx[2].eCountAll(Usr.class));
	}
}

上例是最简单的一个分布式事务演示,包含了数据源配置和 DDL 建表。GTX 事务如果在事务提交中出错,无论有无部分事务提交发生,只要数据库没有down机,在任意时刻网线中断,数据的最终一致性都能保证,不象 XA 协议有可能会有数据不一致的情况发生。

上例 GtxUnlockServ.forceUnlock(ctx[0], result) 方法仅用于单元测试,实际项目中应该去掉这一行,而改成用GtxUnlockServ.start(ctx, loopInterval , maxLoopQty);方法开启一个单独的解锁服务,第二个参数为解锁间隔,单位为秒,必须设置成一个远大于数据库事务超时时间的值,第三个参数可以设为 0,表示没有最大解锁次数限制。

jSqlBox 的分布式事务支持分库、分表,以及指定锁服务器的序号,这样如果没有一个高性能(云)锁服务器,可以通过配置一个锁服务器群来提高事务处理性能,当然这种情况下,锁服务器序号的指定通常是与业务相关的,例如红包转账分布式事务,可以红包的 ID 取模作为锁服务器序号。具体示例请详见 wiki 及单元测试目录下的 GtxShardDbTbLockDbTest.java

jSqlBox 分布式事务的构想几乎和 Seata 项目开源时同时提出,但从完成度来看差不多,这并不表示本人水平高,一个人干翻了 Seata,而是因为与 ORM 工具整合成一体的分布式事务,在编写难度上要远远小于Seata 这种分析 SQl 语法、从底层代理数据源的第三方工具,而且 jSqlBox 的分布式事务只支持一组直连的 DataSource,不提供微服务的调用接口。

本次更新还包括以下内容:

1.  在 demo 目录下演示了使用 Beetl 作为 SQL 模板。例如以下是一个使用 Beetl 模板的调用,通过与 Text 类的结合使用,可以方便地利用 IDE 功能快速定位到模板文本: 

List<Map<String, Object>> usrs = ctx.tQueryForMapList(SelectUsers1.class, bind("age", 50, "name", null));

2.  JAVA8 类中引入 $,a$,c$ 静态简化写法, 例如在 Dao-benchmark 项目中,以下这种 SQL 写法是支持实体字体名重构的:

List<DemoUser> result = gctx().eFindBySample(sample, " or ", $(u::getCode), "=?", param("efg"));

其中的$(u::getCode)静态方法会返回"code"这个实体属性名,也就是数据库的列名(通常约定数据库列名命名与实体属性名一致)

3. JDBPRO 中新增 noNull 方法,用于 like 查询时的非空判断,使用示例:

ctx.iQueryForMapList("select * from a where 1=1",noNull("and name like ?","%",name,"%"));

4. 一些其它改进:

如 TinyTx 类不再使用,改名为 TinyTxAOP,jDialects 模块中 ColumnModel 类中的 length 字段被删除,并修正了 precision 和 scale 在设定浮点数的错误。

5. 另外以下与 jSqlBox 相关的子项目也一并更新到 3.0.0 版,就不再另发更新资讯了,有以下子项目:

jDialects 支持80多种数据库的分页、类型变换、DDL 生成、实体源码生成的数据库方言工具 

jTransactions 一个单独的事务工具,含分布式事务功能

jDbPro 这是 jSqlBox 的内核,建立在 DbUtils 基础上,只有 SQL 功能,不包含 ORM 等功能

MyFat 这是一个 Mybatis 插件,它将 jSqlBox 和 MyBatis 捆绑在一起,利用 jSqlBox 来补足 Mybatis 缺少的实体 CRUD 等功能

期望 | Futures

欢迎发issue提出更好的意见或提交PR,帮助完善jSqlBox

版权 | License

Apache 2.0

原文  https://www.oschina.net/news/109632/jsqlbox-3-0-0-released
正文到此结束
Loading...