一、实现要求
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
(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
@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 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