hsweb的系列學習——hsweb-easy-orm分析

對於此orm框架的封裝,首先要看的便是:hsweb-easy-orm-core

整體思路:因為是為動態表單設計的orm框架,所以要有為映射包裝的PO類和反映到表元素的元數據類MetaData,

以及對這些bean進行操作的各種周邊(最多的也就是curd的細節分拆)

一,映射包裝的PO類

列的設定

我們的curd操作的基本元素首先是表,而我們操作的鐵定就是po類,表中的基本元素首先是列,列包含兩個屬性,名字和欄位類型

那麼對於這個orm框架中的體現就是:

package org.hsweb.ezorm.core.param;/** * @author zhouhao * @since 1.1 */public class Column { private String name; private String type; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Column type(String type) { this.type = type; return this; } public static Column build(String name) { Column column = new Column(); column.setName(name); return column; }}

此處單獨定義了ColumnType,暫時未見使用

package org.hsweb.ezorm.core.param;/** * @author zhouhao * @since 1.1 */public interface ColumnType { String def = "";}

SQL的參數設定

對列的一系列的操作其實是操作欄位及所涉及的數據,那麼我們所要設定的地方有:

通過參考一個sql語句可以知道,操作一個欄位往往會涉及到where後面的條件,以及要處理的欄位如in{...}不處理的欄位 not in{...} 具體代碼裡面的體現就是:

首先對條件類型的設定:

/* * Copyright 2016 http://github.com/hs-web * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.hsweb.ezorm.core.param;/** * 查詢條件類型,用於動態指定查詢條件 * * @author zhouhao * @since 1.0 */public interface TermType { /** * == * * @since 1.0 */ String eq = "eq"; /** * != * * @since 1.0 */ String not = "not"; /** * like * * @since 1.0 */ String like = "like"; /** * not like * * @since 1.0 */ String nlike = "nlike"; /** * > * * @since 1.0 */ String gt = "gt"; /** * < * * @since 1.0 */ String lt = "lt"; /** * >= * * @since 1.0 */ String gte = "gte"; /** * <= * * @since 1.0 */ String lte = "lte"; /** * in * * @since 1.0 */ String in = "in"; /** * notin * * @since 1.0 */ String nin = "nin"; /** * = * * @since 1.0 */ String empty = "empty"; /** * != * * @since 1.0 */ String nempty = "nempty"; /** * is null * * @since 1.0 */ String isnull = "isnull"; /** * not null * * @since 1.0 */ String notnull = "notnull"; /** * between * * @since 1.0 */ String btw = "btw"; /** * not between * * @since 1.0 */ String nbtw = "nbtw"; /** * 此類型將直接執行sql.在類型是從客戶端參數中獲取的場景中,應該屏蔽此類型 * * @see SqlTerm * @since 1.0 * @deprecated 此屬性已棄用,如果想直接拼接sql,請使用 {@link SqlTerm} */ @Deprecated String func = "func";}

其次對於條件的設定:

package org.hsweb.ezorm.core.param;import java.util.LinkedList;import java.util.List;/** * 執行條件 */public class Term implements Cloneable { /** * 欄位 */ private String column; /** * 值 */ private Object value; /** * 鏈接類型 */ private Type type = Type.and; /** * 條件類型 */ private String termType = TermType.eq; /** * 嵌套的條件 */ private List<Term> terms = new LinkedList<>(); public Term or(String term, Object value) { return or(term, TermType.eq,value); } public Term and(String term, Object value) { return and(term, TermType.eq,value); } public Term or(String term, String termType, Object value) { Term queryTerm = new Term(); queryTerm.setTermType(termType); queryTerm.setColumn(term); queryTerm.setValue(value); queryTerm.setType(Type.or); terms.add(queryTerm); return this; } public Term and(String term, String termType, Object value) { Term queryTerm = new Term(); queryTerm.setTermType(termType); queryTerm.setColumn(term); queryTerm.setValue(value); queryTerm.setType(Type.and); terms.add(queryTerm); return this; } public Term nest() { return nest(null, null); } public Term orNest() { return orNest(null, null); } public Term nest(String term, Object value) { Term queryTerm = new Term(); queryTerm.setType(Type.and); queryTerm.setColumn(term); queryTerm.setValue(value); terms.add(queryTerm); return queryTerm; } public Term orNest(String term, Object value) { Term queryTerm = new Term(); queryTerm.setType(Type.or); queryTerm.setColumn(term); queryTerm.setValue(value); terms.add(queryTerm); return queryTerm; } public String getColumn() { return column; } public void setColumn(String column) { if (column == null) return; if (column.contains("$")) { String tmp[] = column.split("[$]"); setTermType(tmp[1]); column = tmp[0]; } this.column = column; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public String getTermType() { return termType.toLowerCase(); } public void setTermType(String termType) { this.termType = termType; } public List<Term> getTerms() { return terms; } public void setTerms(List<Term> terms) { this.terms = terms; } public Term addTerm(Term term) { terms.add(term); return this; } @Override public Term clone() { Term term = new Term(); term.setColumn(column); term.setValue(value); term.setTermType(termType); term.setType(type); terms.forEach(t -> term.addTerm(t.clone())); return term; } public enum Type { or, and; public static Type fromString(String str) { try { return Type.valueOf(str.toLowerCase()); } catch (Exception e) { return and; } } }}

將各種條件用po來表達出來並進行封裝

最後對於SQL參數對象的設定:

package org.hsweb.ezorm.core.param;import java.util.*;import java.util.stream.Collectors;/** * SQL參數對象 * * @author zhouhao * @since 1.0 */public class Param implements Cloneable { /** * 條件 */ protected List<Term> terms = new LinkedList<>(); /** * 指定要處理的欄位 */ protected Set<String> includes = new LinkedHashSet<>(); /** * 指定不處理的欄位 */ protected Set<String> excludes = new LinkedHashSet<>(); public <T extends Param> T or(String column, Object value) { return or(column, TermType.eq, value); } public <T extends Param> T and(String column, Object value) { return and(column, TermType.eq, value); } public <T extends Param> T or(String column, String termType, Object value) { Term term = new Term(); term.setTermType(termType); term.setColumn(column); term.setValue(value); term.setType(Term.Type.or); terms.add(term); return (T) this; } public <T extends Param> T and(String column, String termType, Object value) { Term term = new Term(); term.setTermType(termType); term.setColumn(column); term.setValue(value); term.setType(Term.Type.and); terms.add(term); return (T) this; } public Term nest() { return nest(null, null); } public Term orNest() { return orNest(null, null); } public Term nest(String termString, Object value) { Term term = new Term(); term.setColumn(termString); term.setValue(value); term.setType(Term.Type.and); terms.add(term); return term; } public Term orNest(String termString, Object value) { Term term = new Term(); term.setColumn(termString); term.setValue(value); term.setType(Term.Type.or); terms.add(term); return term; } public <T extends Param> T includes(String... fields) { includes.addAll(Arrays.asList(fields)); return (T) this; } public <T extends Param> T excludes(String... fields) { excludes.addAll(Arrays.asList(fields)); includes.removeAll(Arrays.asList(fields)); return (T) this; } public <T extends Param> T where(String key, Object value) { and(key, value); return (T) this; } public <T extends Param> T where(String key, String termType, Object value) { and(key, termType, value); return (T) this; } public Set<String> getIncludes() { if (includes == null) includes = new LinkedHashSet<>(); return includes; } public Set<String> getExcludes() { if (excludes == null) excludes = new LinkedHashSet<>(); return excludes; } public void setIncludes(Set<String> includes) { this.includes = includes; } public void setExcludes(Set<String> excludes) { this.excludes = excludes; } public List<Term> getTerms() { return terms; } public void setTerms(List<Term> terms) { this.terms = terms; } public <T extends Param> T addTerm(Term term) { terms.add(term); return (T) this; } @Override public Param clone() { Param param = new Param(); param.setExcludes(new LinkedHashSet<>(excludes)); param.setIncludes(new LinkedHashSet<>(includes)); List<Term> terms = this.terms.stream().map(term -> term.clone()).collect(Collectors.toList()); param.setTerms(terms); return param; }}

這裡通過LinkedList 來將條件都包裝到一起 ,通過LinkedHashSet來包裝要處理的欄位

由Param衍生出來的專門對查詢(如分頁)和更新參數的額外封裝:

查詢往往會涉及到排序,這裡對排序先做下封裝:

package org.hsweb.ezorm.core.param;/** * 排序 * * @author zhouhao * @since 1.0 */public class Sort extends Column { private String order = "asc"; private transient QueryParam queryParam; public String getOrder() { return order; } public void setOrder(String order) { this.order = order; } public Sort() { } public Sort(QueryParam queryParam, String name) { this.queryParam = queryParam; setName(name); } public QueryParam asc() { this.order = "asc"; return queryParam; } public QueryParam desc() { this.order = "desc"; return queryParam; } public Sort and(String field) { return queryParam.orderBy(field); } @Override public int hashCode() { return String.valueOf(getName()).concat(order).hashCode(); } @Override public boolean equals(Object obj) { return obj != null && this.hashCode() == obj.hashCode(); }}

QueryParam: 排序欄位同樣LinkedList存儲

package org.hsweb.ezorm.core.param;import java.io.Serializable;import java.util.LinkedHashSet;import java.util.LinkedList;import java.util.List;import java.util.stream.Collectors;/** * Created by 浩 on 2016-01-16 0016. */public class QueryParam extends Param implements Serializable, Cloneable { private static final long serialVersionUID = 7941767360194797891L; /** * 是否進行分頁,默認為true */ private boolean paging = true; /** * 第幾頁 從0開始 */ private int pageIndex = 0; /** * 每頁顯示記錄條數 */ private int pageSize = 25; /** * 排序欄位 * * @since 1.0 */ private List<Sort> sorts = new LinkedList<>(); private boolean forUpdate = false; public QueryParam select(String... fields) { return this.includes(fields); } public Sort orderBy(String column) { Sort sort = new Sort(this, column); sorts.add(sort); return sort; } public <Q extends QueryParam> Q doPaging(int pageIndex) { this.pageIndex = pageIndex; this.paging = true; return (Q) this; } public <Q extends QueryParam> Q doPaging(int pageIndex, int pageSize) { this.pageIndex = pageIndex; this.pageSize = pageSize; this.paging = true; return (Q) this; } public <Q extends QueryParam> Q rePaging(int total) { paging = true; // 當前頁沒有數據後跳轉到最後一頁 if (this.getPageIndex() != 0 && (pageIndex * pageSize) >= total) { int tmp = total / this.getPageSize(); pageIndex = total % this.getPageSize() == 0 ? tmp - 1 : tmp; } return (Q) this; } public boolean isPaging() { return paging; } public void setPaging(boolean paging) { this.paging = paging; } public int getPageIndex() { return pageIndex; } public void setPageIndex(int pageIndex) { this.pageIndex = pageIndex; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public List<Sort> getSorts() { return sorts; } public void setSorts(List<Sort> sorts) { this.sorts = sorts; } public void setForUpdate(boolean forUpdate) { this.forUpdate = forUpdate; } public boolean isForUpdate() { return forUpdate; } @Override public QueryParam clone() { QueryParam sqlParam = new QueryParam(); sqlParam.setExcludes(new LinkedHashSet<>(excludes)); sqlParam.setIncludes(new LinkedHashSet<>(includes)); List<Term> terms = this.terms.stream().map(Term::clone).collect(Collectors.toList()); sqlParam.setTerms(terms); sqlParam.setPageIndex(pageIndex); sqlParam.setPageSize(pageSize); sqlParam.setPaging(paging); sqlParam.setSorts(sorts); sqlParam.setForUpdate(forUpdate); return sqlParam; }}

UpdateParam:

package org.hsweb.ezorm.core.param;import java.util.LinkedHashSet;import java.util.List;import java.util.stream.Collectors;/** * Created by zhouhao on 16-4-19. */public class UpdateParam<T> extends Param { private T data; public UpdateParam() { } public UpdateParam(T data) { this.data = data; } public <C extends UpdateParam<T>> C set(T data) { this.data = data; return (C) this; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public UpdateParam clone() { UpdateParam<T> param = new UpdateParam<>(); param.setData(data); param.setExcludes(new LinkedHashSet<>(excludes)); param.setIncludes(new LinkedHashSet<>(includes)); List<Term> terms = this.terms.stream().map(Term::clone).collect(Collectors.toList()); param.setTerms(terms); return param; }}

對更新數據map類型包裝的數據做擴展:

package org.hsweb.ezorm.core.param;import java.util.HashMap;import java.util.Map;/** * Created by zhouhao on 16-4-21. */public class UpdateMapParam extends UpdateParam<Map<String, Object>> { public UpdateMapParam() { this(new HashMap<>()); } public UpdateMapParam(Map<String, Object> data) { setData(data); } public UpdateMapParam set(String key, Object value) { this.getData().put(key, value); return this; }}

表插入數據的封裝

InsertParam:

package org.hsweb.ezorm.core.param;public class InsertParam<T> { private T data; public InsertParam() { } public InsertParam(T data) { this.data = data; } public InsertParam<T> value(T data) { this.data = data; return this; } public T getData() { return data; } public void setData(T data) { this.data = data; } public static <T> InsertParam<T> build(T data) { return new InsertParam<>(data); }}

和更新針對map數據類型衍生一樣:

package org.hsweb.ezorm.core.param;import java.util.HashMap;import java.util.Map;public class InsertParamMapParam extends InsertParam<Map<String, Object>> { @Override public Map<String, Object> getData() { if (super.getData() == null) setData(new HashMap<>()); return super.getData(); } public InsertParamMapParam value(String property, Object value) { getData().put(property, value); return this; } public InsertParamMapParam values(Map<String, Object> values) { getData().putAll(values); return this; }}

二,反映到表元素的元數據類MetaData

PO轉表元素數據

OptionConverter:選項映射器

package org.hsweb.ezorm.core;/** * 選項映射器,當一個欄位持有映射器時. * <ul> * <li> * 在查詢時,會追加一個名為{@link OptionConverter#getFieldName()}的欄位為{@link OptionConverter#converterValue(Object)} 的值 * </li> * <li> * 在修改或者插入時,驗證器會首先通過 {@link OptionConverter#converterData(Object)}來獲取一個結果. * 如果返回null.則調用{@link OptionConverter#converterValue(Object)} ,並將值放入資料庫. * 如果繼續返回null,則會拋出驗證器異常,提示值不再選項範圍中 * </li> * </ul> * Created by zhouhao on 16-6-4. */public interface OptionConverter { /** * 獲取所有選項 * * @return 選項 */ Object getOptions(); /** * 獲取轉換後的欄位名稱 * * @return 轉換後的欄位名稱 */ String getFieldName(); /** * 將提交的數據,轉換為目標數據 * * @param value 提交的數據 * @return 轉換結果 */ Object converterData(Object value); /** * 將資料庫的數據,轉換為目標數據 * * @param data 資料庫數據 * @return 轉換結果 */ Object converterValue(Object data);}

PropertyWrapper:屬性包裝

package org.hsweb.ezorm.core;import java.io.Serializable;import java.util.Date;import java.util.List;import java.util.Map;public interface PropertyWrapper extends Serializable { <T> T getValue(); String toString(); int toInt(); double toDouble(); boolean isTrue(); Date toDate(); Date toDate(String format); Map<String, Object> toMap(); List<Map> toList(); <T> T toBean(Class<T> type); <T> List<T> toBeanList(Class<T> type); boolean isNullOrEmpty(); boolean valueTypeOf(Class<?> type);}

對PropertyWrapper的簡單實現:

package org.hsweb.ezorm.core;import com.alibaba.fastjson.JSON;import org.hsweb.commons.ClassUtils;import org.hsweb.commons.DateTimeUtils;import org.hsweb.commons.StringUtils;import java.util.Date;import java.util.List;import java.util.Map;/** * Created by zhouhao on 16-6-4. */public class SimplePropertyWrapper implements PropertyWrapper { private Object value; public SimplePropertyWrapper(Object value) { this.value = value; } @Override public <T> T getValue() { return (T) value; } @Override public int toInt() { return StringUtils.toInt(value); } @Override public double toDouble() { return StringUtils.toDouble(value); } @Override public boolean isTrue() { return StringUtils.isTrue(value); } @Override public Date toDate() { if (value instanceof Date) return ((Date) value); return DateTimeUtils.formatUnknownString2Date(toString()); } @Override public Date toDate(String format) { if (value instanceof Date) return ((Date) value); return DateTimeUtils.formatDateString(toString(), format); } @Override public <T> T toBean(Class<T> type) { if (valueTypeOf(type)) return ((T) getValue()); return JSON.parseObject(toString(), type); } @Override public List<Map> toList() { return toBeanList(Map.class); } @Override public Map<String, Object> toMap() { return toBean(Map.class); } @Override public <T> List<T> toBeanList(Class<T> type) { if (getValue() instanceof List) return ((List) getValue()); return JSON.parseArray(toString(), type); } @Override public boolean isNullOrEmpty() { return StringUtils.isNullOrEmpty(value); } @Override public boolean valueTypeOf(Class<?> type) { if (value == null) return false; return ClassUtils.instanceOf(value.getClass(), type); } @Override public String toString() { return String.valueOf(value); }}

ValueConverter:數據轉換

package org.hsweb.ezorm.core;public interface ValueConverter { Object getData(Object value); Object getValue(Object data);}

由其所衍生的類:

這裡只看其中一兩個即可:

ClobValueConverter:

package org.hsweb.ezorm.rdb.meta.converter;import org.hsweb.ezorm.core.ValueConverter;import java.io.Reader;import java.sql.Clob;public class ClobValueConverter implements ValueConverter { @Override public Object getData(Object value) { return value; } @Override public Object getValue(Object data) { if (data instanceof Clob) { Clob clobData = ((Clob) data); try (Reader reader = clobData.getCharacterStream()) { char[] chars = new char[(int) clobData.length()]; reader.read(chars); data = new String(chars); } catch (Exception ignored) { } } return data; }}

BooleanValueConverter:

package org.hsweb.ezorm.rdb.meta.converter;import org.hsweb.ezorm.core.ValueConverter;public class BooleanValueConverter implements ValueConverter { @Override public Object getData(Object value) { return value; } @Override public Object getValue(Object data) { if (null == data) return false; if (data instanceof Boolean) return data; return "1".equals(String.valueOf(data)) || "true".equals(String.valueOf(data)); }}

對象包裝ObjectWrapper:

/* * Copyright 2016 http://github.com/hs-web * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.hsweb.ezorm.core;import java.util.List;/** * 對象包裝器,在執行查詢時,通過包裝器對查詢結果進行初始化 * * @author zhouhao * @since 1.0 */public interface ObjectWrapper<T> { /** * 執行初始化,在sql執行後,包裝結果前,將調用此方法,傳入查詢的列 * * @param columns 列集合 */ default void setUp(List<String> columns) { } <C extends T> Class<C> getType(); /** * 創建對象實例 * * @return 對象實例 */ T newInstance(); /** * 向實例中填充一個屬性值 * * @param instance 實例對象 * @param index 當前實例的索引 * @param attr 屬性名稱 * @param value 屬性值 */ void wrapper(T instance, int index, String attr, Object value); /** * 當一個實例被填充完成後調用,已進行其他操作 * * @param instance 實例對象 */ void done(T instance);}

其衍生的類:

觸發:Trigger:

package org.hsweb.ezorm.core;import java.util.Map;public interface Trigger { void execute(Map<String, Object> context); String select_before = "select.before"; String select_wrapper_each = "select.wrapper.each"; String select_wrapper_done = "select.wrapper.done"; String select_done = "select.done"; String insert_before = "insert.before"; String insert_done = "insert.done"; String update_before = "update.before"; String update_done = "update.done"; String delete_before = "delete.before"; String delete_done = "delete.done";}

定義TableMetaData

package org.hsweb.ezorm.core.meta;import org.hsweb.ezorm.core.ObjectWrapper;import org.hsweb.ezorm.core.PropertyWrapper;import org.hsweb.ezorm.core.Trigger;import java.io.Serializable;import java.util.Map;import java.util.Set;/** * @author zhouhao */public interface TableMetaData extends Serializable { String getName(); String getComment(); String getAlias(); <T extends DatabaseMetaData> T getDatabaseMetaData(); <T extends ColumnMetaData> Set<T> getColumns(); <T extends ColumnMetaData> T getColumn(String name); <T extends ColumnMetaData> T findColumn(String name); <T> ObjectWrapper<T> getObjectWrapper(); PropertyWrapper getProperty(String property); PropertyWrapper getProperty(String property, Object defaultValue); PropertyWrapper setProperty(String property, Object value); void on(String name, Trigger trigger); void on(String name, Map<String, Object> triggerContext); boolean triggerIsSupport(String name);}

裡面包含有表名字,注釋,別名,然後就是表所屬資料庫的元數據信息,表包含列的元數據信息,以及對象的包裝,屬性的包裝,見上面對象包裝,屬性的包裝,以及通過on()方法對Trigger使用

定義DatabaseMetaData

package org.hsweb.ezorm.core.meta;import org.hsweb.ezorm.core.ObjectWrapperFactory;import org.hsweb.ezorm.core.ValidatorFactory;public interface DatabaseMetaData { ObjectWrapperFactory getObjectWrapperFactory(); ValidatorFactory getValidatorFactory(); <T extends TableMetaData> T getTableMetaData(String name);}

個人認為,其實這裡表達的就是銜接的意思,ObjectWrapper和TableMetaData信息的對應(等以後有新的理解了再來修改的)

ValidatorFactory

package org.hsweb.ezorm.core;import org.hsweb.ezorm.core.meta.TableMetaData;public interface ValidatorFactory { Validator createValidator(TableMetaData tableMetaData);//源碼里暫時未使用到該介面方法定義}

Validator其實是對INSERT, UPDATE操作和相應數據進行下驗證

package org.hsweb.ezorm.core;public interface Validator { boolean validate(Object data, Operation operation); enum Operation { INSERT, UPDATE }}

定義ColumnMetaData

package org.hsweb.ezorm.core.meta;import org.hsweb.ezorm.core.OptionConverter;import org.hsweb.ezorm.core.PropertyWrapper;import org.hsweb.ezorm.core.ValueConverter;import java.io.Serializable;import java.util.Set;public interface ColumnMetaData extends Serializable, Cloneable { String getName(); String getAlias(); String getComment(); Class getJavaType(); <T extends TableMetaData> T getTableMetaData(); ValueConverter getValueConverter(); OptionConverter getOptionConverter(); Set<String> getValidator(); PropertyWrapper getProperty(String property); PropertyWrapper getProperty(String property, Object defaultValue); PropertyWrapper setProperty(String property, Object value); <T extends ColumnMetaData> T clone();}

裡面包含有表名字,注釋,別名,然後就是列所屬表的元數據信息,表包含列的元數據信息,以及值的轉換,選項的轉換,屬性的包裝,見上面數據轉換,屬性的包裝小節,以及通過clone()方法返回相應的ColumnMetaData的實現。

因篇幅有點長,增刪改查等相關操作在下一篇中說

原文鏈接:hsweb的系列學習——hsweb-easy-orm分析

推薦閱讀:

後台開發 和 伺服器開發有什麼 異同 ?
響應式編程(上):總覽
Mac下Web開發為為什麼都用Sublime而不用VIM呢?
你的後台夠強大嗎?
使用了https後,還有必要對數據進行簽名來確保數據沒有被篡改嗎?

TAG:后台开发 | 后台 |