Skip to content
This repository has been archived by the owner on Jan 9, 2021. It is now read-only.

YMP框架持久化模块使用详解

suninformation edited this page Aug 23, 2014 · 8 revisions

持久化模块使用详解

如何初始化持久化模块?

持久化模块初始化将完成两部分动作:

  • 初始化所有数据源配置;
  • 创建存储器工厂对象,扫描配置中指定的存储器包并加载存储器实例对象;

调用持久化模块对象进行初始化,代码如下:

// 创建数据源配置对象集合,持久化模块支持多数据源
final Set<DataSourceCfgMeta> _metas = new HashSet<DataSourceCfgMeta>();
_metas.add(new DataSourceCfgMeta("default", 
        DefaultDataSourceAdapter.class.getName(), 
        "com.mysql.jdbc.Driver", 
        "jdbc:mysql://localhost:3306/ymp_db_v1?useUnicode=true&characterEncoding=UTF-8", 
        "root", admin, 
        new HashMap<String, String>()));

// 初始化
JDBC.initialize(new IJdbcConfig() {

    // 是否显示SQL
    public boolean isShowSql() {
        return true;
    }
    // 数据表名前缀
    public String getTablePrefix() {
        return "";
    }
    // 默认数据源名称
    public String getDefaultDataSourceName() {
        return "default";
    }
    // 存储器包集合
    public String[] getRepositoryPackages() {
        return new String[] { "com.abc.repos", "com.def.repos" };
    }
    // 数据源集合
    public Set<DataSourceCfgMeta> getDataSourceCfgMetas() {
        return _metas;
    }

});

如何自定义数据源适配器?

数据源适配器IDataSourceAdapter接口用于实现与数据库的真正连接,通过它可以适配第三方数据库连接池等,框架自身提供了基于DriverManager的默认实现,代码如下:

public class DefaultDataSourceAdapter implements IDataSourceAdapter {

    protected DataSourceCfgMeta cfgMeta;
    
    protected IDialect dialect;
    
    public void initialize(DataSourceCfgMeta cfgMeta) {
        this.cfgMeta = cfgMeta;
        try {
            Class.forName(cfgMeta.getDriverClass());
        } catch (ClassNotFoundException e) {
            throw new Error(RuntimeUtils.unwrapThrow(e));
        }
    }
    
    public Connection getConnection() throws ConnectionException {
        try {
            Connection conn;
            if (cfgMeta.getName() != null) {
                conn = DriverManager.getConnection(cfgMeta.getConnectionUrl(), cfgMeta.getUserName(), cfgMeta.getPassword());
            } else {
                conn = DriverManager.getConnection(cfgMeta.getConnectionUrl());
            }
            return conn;
        } catch (Exception e) {
            throw new ConnectionException(RuntimeUtils.unwrapThrow(e));
        }
    }
    
    public IDialect getDialect() {
        if (dialect == null) {
            try {
                String _prodName = this.getConnection().getMetaData().getDatabaseProductName().toLowerCase();
                if (_prodName.contains("oracle")) {
                    dialect = new OracleDialect();
                } else if (_prodName.contains("mysql")) {
                    dialect = new MySqlDialect();
                } else if (_prodName.contains("sql server")) {
                    dialect = new SQLServer2005Dialect();
                }
            } catch (Exception e) {
                throw new Error(RuntimeUtils.unwrapThrow(e));
            }
        }
        return dialect;
    }
    
    public void destroy() {
    }

}

如何自定义数据库方言?

数据库方言是用于适配不同数据库的不同特性,比如MySQL与Oracle数据库的分页查询方式不同,通过定义方言便可以解决这种问题,框架默认提供MySQL、Oracle和SQL Server 2005三种方言;

自定义数据库方言可以通过实现IDialect接口或继承AbstractDialect抽象类,下面是MySQL方言的代码:

public class MySqlDialect extends AbstractDialect {

    public String getDialectName() {
        return "MySql";
    }
    
    public String getPaginationSql(String sql, int limit, int offset) {
        if (offset == 0) {
            return sql + " limit " + Integer.toString(limit);
        } else {
            return sql + " limit " + Integer.toString(limit) + "," + Integer.toString(offset);
        }
    }

}

如何获取和释放数据源连接?

  • 方式一:直接获取底层Connection对象

    获取默认数据源:

      IConnectionHolder _connHolder = JDBC.getConnectionHolder();
    

    或者获取指定名称的数据源:

      _connHolder = JDBC.getConnectionHolder("test");
      Connection _conn = _connHolder.getConnection();
      ......
      // 释放
      JDBC.release(_connHolder);
    
  • 方式二:由会话(Session)对象维护数据源连接

    获取默认数据源会话:

      ISession _session = JDBC.openSession();
    

    或者获取指定名称的数据源会话:

      _session = JDBC.openSession("test");
      ......
      // 关闭会话
      _session.close();
    

如何定义和操作一个实体对象?

实体对象需要实现IEntity接口,首先定义实体对象基类:

public abstract class BaseEntity<PK> implements IEntity<PK> {

    private static final long serialVersionUID = 1L;
    
    public BaseEntity() {
    }

}

单主键实体定义:

@Entiry(name = "BIZ_FUN")
public class BizFunModel extends BaseEntity<String> {

    private static final long serialVersionUID = 1L;
    
    @Id
    @Property(name = "id")
    private String id;
    
    @Property(name = "name")
    private String name;
    
    @Property(name = "corp_flag")
    private Integer corpFlag;
    
    @Property(name = "op_flag")
    private Integer opFlag;
    
    @Property(name = "biz_code")
    private String bizCode;

    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }

    // 省略Get/Set方法
    ……
}

复合主键实体定义:

  • 定义复合主建对象:

      @PK
      public class CommandCallPK implements IEntityPK {
      
          private static final long serialVersionUID = 1L;
          
          @Property(name = "seq")
          private Integer seq;
          @Property(name = "sim")
          private String sim;
          @Property(name = "time")
          private String time;
    
          // 省略Get/Set方法
          ……
      }
    
  • 实体定义:

      @Property(name = "COMMAND_CALL")
      public class CommandCallModel extends BaseEntity<CommandCallPK> {
      
          private static final long serialVersionUID = 1L;
          
          @Id
          private CommandCallPK commandCallPK;
          
          @Property(name = "commander_type")
          private String commanderType;
          
          @Property(name = "protocol_name")
          private Integer protocolName;
          
          @Property(name = "code")
          private Integer code;
          
          @Property(name = "message")
          private String message;
    
          public CommandCallPK getId() {
              return id;
          }
          
          public void setId(CommandCallPK id) {
              this.id = id;
          }
    
          // 省略Get/Set方法
          ……
      }
    

如何执行数据库操作?

  • 查询

      ISession _session = JDBC.openSession();
      try{
          // 指定条件查询BizFunModel实体,并仅返回id字段
          List<BizFunModel> _result = _session.findAll(BizFunModel.class,"corp_flag > ?", new Object[] { "123" }, "id");
      } finally {
          _session.close();
      }
    
  • 更新

      ISession _session = JDBC.openSession();
      try{
          //
          BizFunModel _biz = new BizFunModel();
          _biz.setId("123");
          _biz.setCorpFlag(2);
          // 仅仅更新corp_flag字段
          _session.update(_biz, "corp_flag");
      } finally {
          _session.close();
      }
    
  • 插入

      ISession _session = JDBC.openSession();
      try{
          //
          BizFunModel _biz = new BizFunModel();
          _biz.setId("123");
          _biz.setCorpFlag(2);
          //
          _session.insert(_biz);
      } finally {
          _session.close();
      }
    
  • 删除

      ISession _session = JDBC.openSession();
      try{
          // 删除Id=123的记录
          _session.delete(BizFunModel.class, "123");
      } finally {
          _session.close();
      }
    

如何通过编码方式创建和使用SQL组合查询?

  • 首先,创建组合查询对象

      public class CompositeQueryDemo<T> extends AbstractCompositeQuery<T> {
      
          public CompositeQueryDemo(IResultSetHandler<T> handler) {
              super(handler);
          }
          
          public CompositeQueryDemo(IResultSetHandler<T> handler, IConnectionHolder conn) {
              super(handler, conn);
          }
          
          public void __doInitCompositeQuery() {
              addCondition(createCondition(ModelModel.TABLE_NAME, "m")
                      .addColumn(ModelModel.FIELDS.NAME)
                      .addColumn(ModelModel.FIELDS.SPEC_IDS).setJoinType(JoinType.LEFT_JOIN));
              addCondition(createCondition(ModelAttributeModel.TABLE_NAME, "attr")
                      .addColumn(ModelAttributeModel.FIELDS.NAME, "attrName")
                      .addColumn(ModelAttributeModel.FIELDS.VALUE)
                      .setOnCondition("attr." + ModelAttributeModel.FIELDS.MODEL_ID + "=m." + ModelModel.FIELDS.ID));
          }
      
      }
    
  • 执行查询

      public PageResultSet<Object[]> findByCmpQuery(String where, Object[] params, int page, int pageSize) throws Exception {
          ISession session = JDBC.openSession();
          try {
              CompositeQueryDemo cmpQuery = new CompositeQueryDemo(new ArrayResultSetHandler(), session.getConnection());
              return cmpQuery.doQuery(where, params, pageSize, page);
          } finally {
              session.close();
          }
      }
    
  • 最终编译执行的SQL语句

      SELECT
      	m.NAME,
      	m.spec_ids,
      	attr.NAME AS attrName,
      	attr.VALUE
      
      FROM
      	tr_model m
      LEFT JOIN tr_model_attribute attr ON (attr.model_id = m.id)
      LIMIT 0, 2
    

如何定义和使用存储器?

  • 首先定义存储器业务接口:

      public interface IAuthenticationRepository {
      
          public String sayHello(String uname);
    
      }
    
  • 实现存储器业务接口,声明并注册:

      @Repository
      public class AuthenticationRepository implements IAuthenticationRepository {
      
          public String sayHello(String uname) {
              return "hello " + uname;
          }
      
      }
      // 注册存储器
      JDBC.registerBean(AuthenticationRepository.class);
    
  • 获取存储器实例,调用业务方法:

      IAuthenticationRepository _repo = JDBC.getBean(IAuthenticationRepository.class);
      _repo.sayHello("to me");
    

如何使用数据库事务?

  • 显式事务处理

    • 有返回值:

        return Trans.exec(new TransResultTask<Integer>() {
            public void doTask() {
                ISession _session =null;
                try {
                    _session =  JDBC.openSession();
                    int result = _session.delete(BizFunModel.class, "123");
                    // 设置返回值
                    this.setResult(result);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    _session.close();
                }
            }
        });
      
    • 无返回值:

        Trans.exec(new ITransTask() {
            public void doTask() {
                ISession _session =null;
                int result;
                try {
                    _session =  JDBC.openSession();
                    _session.delete(BizFunModel.class, "123");
                    // 设置返回值
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    _session.close();
                }
            }
        });
      
  • 声明式事务处理

    • 在定义存储器接口方法时添加@Transaction注解,如:

        public interface ITransRepository {
        
            @Transaction
            public String sayHello(String uname);
      
            @Transaction(TransactionLevel.TRANSACTION_READ_COMMITTED)
            public String sayHi(String uname);
      
        }
      
    • 需要通过JDBC.getProxyBean(...)获取存储器实例,如:

        ITransRepository _repo = JDBC.getProxyBean(ITransRepository.class);
        _repo.sayHi();
      

    通过以上调整,存储器内被@Transaction注解声明的方法将被开启事务并与显式事务处理过程一样支持事务嵌套。

    注:@Transaction注解必须声明在接口方法上;