17. 内置支持主从数据库

BeetlSql管理数据源,如果只提供一个数据源,则认为读写均操作此数据源,如果提供多个,则默认第一个为写库,其他为读库。用户在开发代码的时候,无需关心操作的是哪个数据库,因为调用sqlScript 的 select相关api的时候,总是去读取从库,add/update/delete 的时候,总是读取主库(如下是主从实现原理,大部分情况下无需关心如何实现)

  1. sqlManager.insert(User.class,user) // 操作主库,如果只配置了一个数据源,则无所谓主从
  2. sqlManager.unique(id,User.class) //读取从库

主从库的逻辑是由ConnectionSource来决定的,如下DefaultConnectionSource 的逻辑

  1. @Override
  2. public Connection getConn(String sqlId,boolean isUpdate,String sql,List<?> paras){
  3. if(this.slaves==null||this.slaves.length==0) return this.getWriteConn(sqlId,sql,paras);
  4. if(isUpdate) return this.getWriteConn(sqlId,sql,paras);
  5. int status = forceStatus.get();
  6. if(status ==0||status==1){
  7. return this.getReadConn(sqlId, sql, paras);
  8. }else{
  9. return this.getWriteConn(sqlId,sql,paras);
  10. }
  11. }
  • forceStatus 可以强制SQLManager 使用主或者从数据库。参考api SQLManager.useMaster(DBRunner f) ,SQLManager.useSlave(DBRunner f)
    对于不同的ConnectionSource 完成逻辑不一样,对于spring,jfinal这样的框架,如果sqlManager在事务环境里,总是操作主数据库,如果是只读事务环境 则操作从数据库。如果没有事务环境,则根据sql是查询还是更新来决定。

如下是SpringConnectionSource 提供的主从逻辑

  1. public Connection getConn(String sqlId,boolean isUpdate,String sql,List paras){
  2. //只有一个数据源
  3. if(this.slaves==null||this.slaves.length==0) return this.getWriteConn(sqlId,sql,paras);
  4. //如果是更新语句,也得走master
  5. if(isUpdate) return this.getWriteConn(sqlId,sql,paras);
  6. //如果api强制使用
  7. int status = forceStatus.get();
  8. if(status==1){
  9. return this.getReadConn(sqlId, sql, paras);
  10. }else if(status ==2){
  11. return this.getWriteConn(sqlId,sql,paras);
  12. }
  13. //在事物里都用master,除了readonly事物
  14. boolean inTrans = TransactionSynchronizationManager.isActualTransactionActive();
  15. if(inTrans){
  16. boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
  17. if(!isReadOnly){
  18. return this.getWriteConn(sqlId,sql,paras);
  19. }
  20. }
  21. return this.getReadConn(sqlId, sql, paras);
  22. }

注意,对于使用者来说,无需关心本节说的内容,仅仅供要定制主从逻辑的架构师。