1、實現要求java
一、數據訪問層的職責是對數據庫進行增刪改查的操做,因此能夠很是單一,僅僅只須要一個inteface便可搞定;git
二、全自動ORM不利於SQL的優化與SQL的定製,因此TeaFrameWork ORM準備用半自動的方式實現,開發人員須要寫SQL;sql
三、告別配置文件,純註解;數據庫
四、接口每一個方法只須要一個參數,能夠是PO對象、能夠是MAP、能夠是8大基本數據類型+String和Date安全
五、動態綁定SQL框架
六、支持Oracle、Mysqlide
七、自動分頁工具
八、佔位符用#號,如:#id#,#name#,這個和ibatis一致優化
2、分析.net
一、設計上ORM只須要inteface,那麼具體實現類必須由框架生成,對比了不少字節碼生成工具以後,決定採用cglib
二、對數據庫的操做,本質上只有兩種:讀、寫。
(1)、讀即Select語句,返回值類型:8大基本數據類型+String和Date、PO對象、List<PO對象>、List<Map<String,Object>>,這裏咱們限定了返回的類型,基本上知足了平常開發
(2)、寫:insert、update、delete等,insert操做中有個主鍵獲取問題,能夠自動生成,也能夠寫SQL得到,例如Oracle的select s_users.nextval from dual
3、具體實現
一、註解
(1)、@TeaDao標示這個inteface是數據訪問層,讓bean容器啓動時能夠掃描到
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface TeaDao { public String value() default ""; }
(2)、@SQL給具體方法綁定SQL語句,如:@SQL("select * from users") public List<User> getAllUser();
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface SQL { public String value(); }
(3)、@GetPrimaryKey註解生成主鍵的語句,一般和insert語句配合使用
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface GetPrimaryKey { public String sql(); public String primaryKeyProperty(); }
如:
@GetPrimaryKey(sql = "select s_users.nextval from dual", primaryKeyProperty = "id") @SQL("insert into users(id,name,password,createdate) values(#id#,#name#,#password#,#createdate#)") public int add(Map<String, Object> map);
(4)、@AutoIncrement註解新增時由數據庫自動生成主鍵,和新增配合使用
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AutoIncrement { }
(5)、@DynamicSQL註解方法的SQL是動態傳入,在查詢場景下使用
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DynamicSQL { }
二、代理類OrmProxy,具體攔截inteface的方式,執行SQL
public class OrmProxy implements InterfaceExecutor { private final static String SELECT = "select"; private final static String INSERT = "insert"; private static OrmProxy instance; private OrmProxy() { } public synchronized static OrmProxy getInstance() { if (instance == null) { instance = new OrmProxy(); } return instance; } @Override public Object invoke(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { if (method.getDeclaringClass().equals(java.lang.Object.class)) { return proxy.invokeSuper(obj, args); } if (!method.isAnnotationPresent(SQL.class) && !method.isAnnotationPresent(DynamicSQL.class)) { throw new TeaOrmException("沒有綁定SQL"); } if (args != null && args.length > 1) { throw new TeaOrmException("只能傳遞一個參數"); } if (method.isAnnotationPresent(GetPrimaryKey.class) && method.isAnnotationPresent(AutoIncrement.class)) { throw new TeaOrmException("GetPrimaryKey和AutoIncrement不能同時註解在一個方法上"); } if (method.getAnnotation(SQL.class) != null && method.getAnnotation(DynamicSQL.class) != null) { throw new TeaOrmException("SQL和DynamicSQL不能同時註解在一個方法上"); } if (TranscationThreadVariable.get() == null || !TranscationThreadVariable.get()) { if (ConnectionThreadVariable.getConnetion() == null) { ConnectionThreadVariable.setConnetion(DataSourceHelp.getConnection()); } } try { if (method.isAnnotationPresent(SQL.class)) { String sql = method.getAnnotation(SQL.class).value().trim(); AbstractDataBind dataBind = DataBindFactory .getDataBind(args == null || args.length == 0 ? null : args[0]); if (!SELECT.equalsIgnoreCase(sql.substring(0, 6))) { boolean isAutoIncrement = false; if (INSERT.equalsIgnoreCase(sql.substring(0, 6))) { if (method.getAnnotation(AutoIncrement.class) != null) { isAutoIncrement = true; } if (method.getAnnotation(GetPrimaryKey.class) != null) { Object object = dataBind.fillPrimaryKey(method, args[0]);// 填充主鍵 dataBind.excuteUpdate(sql, args == null || args.length == 0 ? null : args[0], isAutoIncrement); return object; } } return dataBind.excuteUpdate(sql, args == null || args.length == 0 ? null : args[0], isAutoIncrement); } else { return QueryResultProcesser.createQueryResult( dataBind.excuteQuery(sql, args == null || args.length == 0 ? null : args[0]), method); } } else if (method.isAnnotationPresent(DynamicSQL.class)) { String sql = DynamicSqlUtil.get() == null ? null : DynamicSqlUtil.get().trim(); if (null == sql || "".equals(sql)) { throw new TeaOrmException("SQL語句不能爲空"); } if (sql.length() < 6 || !SELECT.equalsIgnoreCase(sql.substring(0, 6))) { throw new TeaOrmException("只能綁定select語句"); } return QueryResultProcesser.createQueryResult(DataBindFactory.getDataBind(null).excuteQuery(sql, args == null || args.length == 0 ? null : args[0]), method); } } catch (Exception e) { throw new TeaOrmException(e); } finally { if (TranscationThreadVariable.get() == null || !TranscationThreadVariable.get()) { ConnectionThreadVariable.getConnetion().close(); ConnectionThreadVariable.clearThreadVariable(); } } return null; } }
代碼解釋:
(1)、用於都是method執行,沒有共享變量,因此沒有線程安全問題,故而這裏用單例模式
(2)、獲取SQL分兩大塊,動態SQL和固定註解SQL,動態SQL只用於查詢場景。對於新增,須要獲取主鍵生成方案(sql生成和自動生成)
(3)、InterfaceExecutor接口會在Bean容器設計中詳細講到
(4)、Transcation相關的代碼,會在事務設計中詳細講到
自此,一個ORM核心的代碼已經完成,剩下SQL的執行過程、佔位符替換,請關注《TeaFramework——ORM框架的實現(二)》
項目地址:https://git.oschina.net/lxkm/teaframework
博客:https://my.oschina.net/u/1778239/blog