皇上,还记得我吗?我就是1999年那个Linux伊甸园啊-----24小时滚动更新开源资讯,全年无休!

jSqlBox 2.0.6 发布,轻松解决分库分表事务

jSqlBox 是一个 Java 持久层工层,2.0.6 版发布,主要有两个更新:

1. 添加了对以下三个 JPA 注解的支持:
@Version 乐观锁注解
@Enumerated 枚举字段注解
@Convert 自定义字段转换器

具体的用法可参考 jSqlBox 的 [ 用户手册 ],对于以操纵 SQL 为基础的常见 DAO 工具来说,jSqlBox 可能算是支持标准 JPA 注解比较多的了,目前它已支持的 JPA 注解达到以下 15 个,这些注解既可以使用 JPA 的,也可以使用 jSqlBox 自带的同名注解:

“`
@GeneratedValue,@GenerationType,@Id,@Index,@SequenceGenerator,@Table,@TableGenerator,@Transient,@UniqueConstraint,@Version,@Column,@Convert,@Entity,@Enumerated,@EnumType

“`
至于完整的 JPA 注解有上百个,不可能也没必要完全去支持,因为作者本人认为 JPA 从 Hibernate 演化而来,本身就存在着基于容器、过度复杂的问题。

2. 更正了多数据源及分库情况下不支持事务的 bug:

在 jSqlBox2.0.5 及之前版本,存在一个 bug:当 SqlBoxContext 实体被作为参数传递时, 例如:

ctx1.iExecute(ctx2,'insert into users (userid,name) value(...)'

它将会从 ctx1 切换到 ctx2 上工作,也就是数据源被切换了,但问题是,如果上述语句是在一个@TX 之类的自定义声明式事务注解控制下,它的事务怎么处理? 2.0.5 版及以前对这个问题考虑不周,这种情况下 ctx2 依然工作在自动提交模式,不受事务控制,显然这是一个严重的 Bug。

从 2.0.6 版起,jSqlBox 在 jTransaction 模块(子项目) 增加了一个 GroupTx 类,这个类的作用是实现多数据源事务,解决了 SqlBoxContext 被作为参数传递时不受事务控制的问题。这个 GroupTx 事务不是分布式事务,只保证一致回滚,但不保证一致提交,也就是说,当有一组 SqlBoxContext 同时在 GroupTx 控制的方法里进行数据存取时,是不安全的,但是如果只有其中任意一个(具体那一个不知道) 数据源进行提交,则是 100%数据一致性安全的。 GroupTx 最适用的场合是 Sharding,因为在 Sharding 场景下,任一个 SQL 都会可能在一群 SqlBoxContext 中根据实体的 Sharding 键 (jSqlBox 要求是主键) 切换,但是因为业务优化设计的原因,通常所有相关的业务数据都会保存在同一个数据库中,也就是说会收敛到单个数据源上进行操作,也就充分发挥了 GroupTx 的特点,实现了安全性。 Sharding 和分布式事务都是用来解决大数据量的手段, 如果业务采用了 Sharding 方案,往往可以在支持大数据量的前题下,不制造出分布式事务问题,当然这对业务设计、持久层工具的要求比较高,所有业务、实体和 SQL 存取都要考虑分库分表情况,和开发单机应用不太一样。如果采用微服务架构,把所有订单放在一个库里,所有用户放在另一个库里,则编程虽然简单,但却制造出了微服务之间的分布式事务问题。

GroupTx 的使用示例如下:(完整源码见 jSqlBox/core/目录下的 GroupTxTest.java)

public class GroupTxTest {
	GroupTxConnectionManager gm;
	HikariDataSource ds1;
	HikariDataSource ds2;
	SqlBoxContext ctx1;
	SqlBoxContext ctx2;

	@Before
	public void init() {
		ds1 = new HikariDataSource();
                (略去部分内容) 

		gm = new GroupTxConnectionManager(ds1, ds2); 
		ctx1 = new SqlBoxContext(ds1);
		ctx1.setConnectionManager(gm);//设定 GroupTxConnectionManager

		ctx2 = new SqlBoxContext(ds2);
		ctx2.setConnectionManager(gm);//设定 GroupTxConnectionManager
	} 

	@Test
	public void groupRollbackTest() { // test group roll back
		for (int i = 0; i < 100; i++) {
			gm.startGroupTransaction();
			try {
				Assert.assertEquals(100, ctx1.eCountAll(Usr.class));
				new Usr().putField("firstName", "Foo").insert(ctx1);
				Assert.assertEquals(101, ctx1.eCountAll(Tail.class, tail("users")));

				Assert.assertEquals(100, ctx2.eCountAll(Usr.class));
				new Usr().putField("firstName", "Foo").insert(ctx2);
				Assert.assertEquals(101, ctx2.eCountAll(Tail.class, tail("users")));
				System.out.println(1 / 0); // 除 0,事务回滚!
				gm.commitGroupTx();
			} catch (Exception e) {
				gm.rollbackGroupTx();
			}
			Assert.assertEquals(100, ctx1.eCountAll(Tail.class, tail("users")));
			Assert.assertEquals(100, ctx2.eCountAll(Tail.class, tail("users")));
		}
	}

	@Test
	public void groupCommitTest() { // test group commit
		for (int i = 0; i < 100; i++) {
			gm.startGroupTransaction();
			try {
				new Usr().putField("firstName", "Foo").insert(ctx1);
				ctx1.eInsert(new Usr().setFirstName("Foo"), ctx2);
				new Usr().putField("firstName", "Bar").insert(ctx2);
				gm.commitGroupTx(); //两个都提交, 但不可信任,因为。。。
			} catch (Exception e) {
				gm.rollbackGroupTx();
			}
		} 
	}

	@Test
	public void groupPartialCommitTest() { // simulate partial commit test
		Assert.assertEquals(100, ctx1.eCountAll(Tail.class, tail("users")));
		gm.startGroupTransaction();
		try {
			new Usr().putField("firstName", "Foo").insert(ctx1);
			new Usr().putField("firstName", "Foo").insert(ctx2);
			ds2.close();//看到没有,ds2 关闭了,但是 ctx1 还是提交成功了
			gm.commitGroupTx();
		} catch (Exception e) {
			gm.rollbackGroupTx();
		}
		Assert.assertEquals(101, ctx1.eCountAll(Tail.class, tail("users")));
	}
}

以上示例演示了手工进行 GroupTx 事务的开启、提交和回滚,示例说明了如果有两个 ctx 同时进行提交,在调用 commitGroupTx 时,如果运行期异常发生,会回步回滚,这个没有问题,但是如果一个数据提交出错,另一个没有,就可能造成部分提交的情况,这就是 GroupTx 的局限,所以只能用在多打一的情况下,也就是说通常最适合它的场景是 Sharding 事务,最终生效的实际数据源会收敛到单个数据源上操作。

以上是手工进行 GroupTx 事务的配置和解说,关于声明式方式的配置使用和解说因篇幅问题请详见 jSqlBox 的 wiki 用户手册。

jSqlBox 下一步计划是加入分布式 TCC 事务支持,补上这项一直缺少的功能,初步打算是加入 Fescar 的支持,这是阿里最近开源出来的项目,还没成熟,它的特点是对业务入侵小,通过分析 SQL 来自动创建回滚 SQL。 不过个人感觉 Fescar 可能会存在性能问题和兼容性问题(非常类似于 jdbc-sharding 所面对的问题,它也是通过分析 SQL 语法来支持 Sharding),如果由持久层工具来创建回滚 SQL,虽然麻烦一点,但可能通用性和性能会更好一点。

作者主要开源项目 | Other Projects

期望 | Futures

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

版权 | License

Apache 2.0

关注我 | About Me

Github
码云

转自 https://www.oschina.net/news/104447/jsqlbox-2-0-6-released

分享到:更多 ()