TeaFramework——ORM框架的實現(一)

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 

相關文章
相關標籤/搜索