TeaFramework——ORM框架的实现(一)

一、实现要求

1、数据访问层的职责是对数据库进行增删改查的操作,所以可以非常单一,仅仅只需要一个inteface即可搞定;

2、全自动ORM不利于SQL的优化与SQL的定制,所以TeaFrameWork  ORM准备用半自动的方式实现,开发人员需要写SQL;

3、告别配置文件,纯注解;

4、接口每个方法只需要一个参数,可以是PO对象、可以是MAP、可以是8大基本数据类型+String和Date

5、动态绑定SQL

6、支持Oracle、Mysql

7、自动分页

8、占位符用#号,如:#id#,#name#,这个和ibatis一致

二、分析

1、设计上ORM只需要inteface,那么具体实现类必须由框架生成,对比了很多字节码生成工具之后,决定采用cglib

2、对数据库的操作,本质上只有两种:读、写。

(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

三、具体实现

1、注解

(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 {

}

2、代理类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://blog.csdn.net/dong_lxkm

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值