【ORM】怎样自己写一个ORM框架-2

接上文,我们获取到了连接,需要封装成查询类进行查询操作

我们就叫它BillQuery

BillQuery

举个栗子,我们通过主表主键来执行查询操作。其实保证唯一性

我们规定:public class BillQuery实例化的时候必须要使用泛型,
public BillQuery(Class<E> clazz)

构造参数必须传入对应的class
每个pojo都是基于元数据来得,所以在实例化BillQuery的时候,我们把一些信息注入到类内部,
在查询的时候可以获取到主表的SinglePojo和子表的(List)SinglePojo.
查询策略是先查主表,主表的主键就是子表的外键,再查询子表

我们通过的是主表主键的List数组来进行查询,这个时候会有一个问题,一般情况下,我们用in的语句来进行数组数据的查询,这个时候会有效率问题。我们设置Max的in的数量为100,如果超过100的数量,我们采取创建临时表的方式来查询

private ISinglePojo[] query(Class<? extends ISinglePojo> clazz,
  IAttributeMeta field, TableIDQueryCondition conditionBuilder) {
IAttributeMeta[] feilds = new IAttributeMeta[] {
  field
};
String condition = conditionBuilder.build(feilds);

SinglePojoQuery query = new SinglePojoQuery(clazz);
ISinglePojo[] pojos = query.queryWithWhereKeyWord(condition, null);
IVOMeta meta = null;
if (pojos.length > 0) {
  meta = pojos[0].getMetaData();
}
else {
  ISinglePojo pojo = Constructor.construct(clazz);
  meta = pojo.getMetaData();
}
String message = "从数据库中加载【" + meta.getLabel();
message = message + "】数据" + pojos.length; 
return pojos;}

我们使用对于SinglePojo适用的query方法进行查询主表数据

private String constructSQL(IAttributeMeta[] attributeMetas,String wheresqlpart, String condition, String order) {
ITableMeta[] tables =
    this.getTables(attributeMetas, wheresqlpart, condition, order);

SqlBuilder sql = new SqlBuilder();
sql.append(" select ");
// 要查询的字段
sql.append(this.constructQueryField(attributeMetas, tables.length > 1));
sql.append(" from ");
// 要查询的表
for (ITableMeta table : tables) {
  sql.append(table.getName());
  sql.append(",");
}
sql.deleteLastChar();

if (wheresqlpart == null) {
  sql.append(" where ");
}
else {
  sql.append(" ");
  sql.append(wheresqlpart);
  sql.append(" and ");
}

IAttributeMeta keyMeta = this.voMeta.getPrimaryAttribute();
// 看是否有主键。如果没有主键,则当前元数据也不可能有扩展表。因为没有对应的主键存在
if (keyMeta.getColumn() != null) {
  ITableMeta mainTable = keyMeta.getColumn().getTable();
  // 表间连接语句,包含了dr=0
  String connectSql = this.constructTableConnecSQL(tables, mainTable);
  sql.append(connectSql);
}
else {
  sql.append(tables[0].getName());
  sql.append(".");
  sql.append("dr=0 ");
}



// 额外的条件
if (condition != null) {
  sql.append(" ");
  sql.append(condition);
}

// 排序语句
if (order != null) {
  sql.append(" ");
  sql.append(order);
}

return sql.toString();  }  

我们的sql的组装就通过元数据+pojo类已经完成

这个时候我们刚刚说的数据库连接就用到了

public IRowSet query(String sql) {

DBTool tool = new DBTool();
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
List<Object[]> list = new ArrayList<Object[]>();
int count = -1;
int rowcount = 0;
try {
  connection = tool.getConnection();
  stmt = connection.createStatement();

  // 设置结果集
  if (this.maxRows> 0) {
      stmt.setMaxRows(this.maxRows);
  }else {
      stmt.setMaxRows(0);
  }

  rs = stmt.executeQuery(sql);
  count = rs.getMetaData().getColumnCount();
  while (rs.next()) {
    Object[] rows = new Object[count];
    for (int i = 0; i < count; i++) {
      rows[i] = rs.getObject(i + 1);
    }
    list.add(rows);
    rowcount++;
    if (this.maxRows != DataAccessUtils.MAX_ROWS
        && rowcount >= this.maxRows) {
      break;
    }
  }
}
catch (SQLException ex) {
  TransferSqlException e = new TransferSqlException(ex, sql);
  ExceptionUtils.wrappException(e);
}
finally {
  this.closeDB(connection, stmt, rs);
}
int size = list.size();
Object[][] data = new Object[size][count];
data = list.toArray(data);
IRowSet rowSet = new RowSet(data);
return rowSet;
 }

我们使用PrepareStatement 来进行实际的预编译查询,减少了sql注入的风险,也有助于提高批量查询的执行效率

执行完成,用RowSet包装返回数据
这个时候我们需要将返回的RowSet与我们的SinglePojo进行匹配转换

public E[] convert(IRowSet rowset) {
int size = rowset.size();
E[] pojos = Constructor.construct(this.clazz, size);
int cursor = 0;
int length = this.names.length;
while (rowset.next()) {
    for (int i = 0; i < length; i++) {
        Object value = rowset.getObject(i);
        //BLOB类型的JavaType转换为Object,数据比较大时,oracle会转换成LONG存储,超过LONG的范围会报错,
        //因此,判断Object类型数据是String时,转换为byte[]来进行存储,见类DataAccessUtils
        //查询出来后如属性原始类型为BLOB,需要将byte[]再转为String,如为其它类似TYPE_IMAGE,则不需要转为String

        if (pojos[cursor].getMetaData() == null) {
            pojos[cursor].setAttributeValue(this.names[i], value);
        } else {
            IAttributeMeta attribute = pojos[cursor].getMetaData().getAttribute(this.names[i]);
            if (attribute != null && attribute.getModelType() == IType.TYPE_BLOB && value instanceof byte[]) {
                value = new String((byte[])value);
            }
            pojos[cursor].setAttributeValue(this.names[i], value);
        }
      }
      cursor++;

}
return pojos;
}

这样就实现了返回值RowSet与实体类的赋值操作,实际我们能看出,我们的Mapping是放在了元数据来实现的,也就是最开始的PowerDesign来实现

这样主表的pojo我们赋值完毕,查询子表的方式类似,要考虑多子表的情况稍微复杂一点点
最后,我们需要将主表pojo和子表pojo组合成AggPojo

public E[] composite() {
IVOMeta parentMeta = this.billMeta.getParent();

IVOMeta[] childrenMeta = this.billMeta.getChildren();

List<E> list = new ArrayList<E>();
MapList<String, SinglePojo> index = this.voIndex.get(parentMeta);
Set<Entry<String, List<SinglePojo>>> entryset = index.entrySet();
for (Entry<String, List<SinglePojo>> entry : entryset) {
  String pk = entry.getKey();
  SinglePojo parent = entry.getValue().get(0);
  E bill = Constructor.construct(this.billClass);
  bill.setParent(parent);
  for (IVOMeta childMeta : childrenMeta) {
    SinglePojo[] pojos = this.construct(pk, childMeta);
    bill.setChildren(childMeta, pojos);
  }
  list.add(bill);
}

E[] bills = null;
if (list.size() == 0) {
  bills = Constructor.declareArray(this.billClass, 0);
}
else {
  ListToArrayTool<E> tool = new ListToArrayTool<E>();
  bills = tool.convertToArray(list);
}
return bills;}

这样实现了一个nosql的查询,这些过程对于使用者来说都是透明的
现在基本实现了对于数据的查询

有些时候,业务数据比较多的情况下,粗粒度的查询难免会导致查询的数据过多,影响查询效率和用户的友好程度,而且这样的查询在主子表的情况下没有做任何筛选就全查出来了,可能有些子表数据我们不关心,所以细粒度没有精细到表体行。

所以我们有了懒加载的查询,和针对子表查询返回主子表数据的ViewPojo查询。下次分享

猜你喜欢

转载自blog.csdn.net/qq_34370153/article/details/80929834