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

开发四年只会写业务代码,分布式高并发都不会还做程序员? >>>  hot3.png

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

  • 基于DbUtils内核的持久层工具 jSqlBox

  • 数据库方言工具 jDialects

  • 独立的声明式事务工具 jTransactions

  • 微型IOC/AOP工具 jBeanBox

  • 服务端布局工具 jWebBox

期望 | Futures

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

版权 | License

Apache 2.0

关注我 | About Me

Github
码云

你可能感兴趣的:(jSqlBox 2.0.6 发布,轻松解决分库分表事务)