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

xiaoxiao2021-02-28  12

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

我们就叫它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查询。下次分享

转载请注明原文地址: https://www.6miu.com/read-2506018.html

最新回复(0)