拿起笔来做刀枪 · 之六 再造一个hibernate

hibernate有两个比较有趣的地方:

一个是它的core feature : ORM。

还有一个是它的HQL 与多种sql语法体系的映射。

第二个部分其实我们很少用到,而且相对比较复杂,涉及到词法分析、语法树构建等一系列编译原理的天坑。

我们还是从最直观的入手:ORM。

先从我们第一直观印象,构建我们的需求接口:

package net.csdn.blog.deltatang.hibernate4me;

public abstract class SqlEngine {

    public abstract void createTable(Class<? extends DbValObj> clazz);
    
    public abstract void insert(DbValObj valObj);
    
    public abstract DbValObj load(int id, Class<? extends DbValObj> clazz);

    public void test() {
		
		createTable(Document.class);
		
		Document doc1 = new Document(1, "解放区的天是明朗的天", "Document 1 的正文内容");
		Document doc2 = new Document(2, "从来就没有什么救世主,也没有神仙皇帝", "Document 2 的正文内容");
		Document doc3 = new Document(3, "中国人民从此站立起来了", "Document 3 的正文内容");
		Document doc4 = new Document(4, "东风吹,战鼓擂,如今世上谁怕谁", "Document 4 的正文内容");
		Document doc5 = new Document(5, "不是东风压倒西风,就是西风压倒东风", "Document 5 的正文内容");
		Document doc6 = new Document(6, "人民万岁,人民万岁", "Document 6 的正文内容");
		
		insert(doc1);
		insert(doc2);
		insert(doc3);
		insert(doc4);
		insert(doc5);
		insert(doc6);
		
		for(int i = 1; i < 7; i++) {
			Document doc = (Document) load(i, Document.class);
			System.out.println(doc);
		}
		
	}

}

以上代码,提供了直观的需求:不用写sql语句,就能通过对象,和数据库直观的联系起来。

当然还涉及到我们自定义的两个类:

package net.csdn.blog.deltatang.hibernate4me;

public interface DbValObj {

}

这个接口没有特殊含义,仅仅是一个分类或者描述功用的标志,就跟
java.io.Serializable
是一样的作用,好像叫做什么模式来着? java 的模式太特么泛滥了,俺实在想不起来了:)

在就是 Document

package net.csdn.blog.deltatang.hibernate4me;

public class Document implements DbValObj {
	
	private int id;
	
	private String title;
	
	private String content;

        public Document() {
            super();
        }

        public Document(int id, String title, String content) {
		super();
		this.id = id;
		this.title = title;
		this.content = content;
	}

	public int getId() {
		return id;
	}

	public String getTitle() {
		return title;
	}

	public String getContent() {
		return content;
	}

	@Override
	public String toString() {
		return "Document [id=" + id + ", title=" + title + ", content=" + content + "]";
	}
	
}
这个前文有提到,检索的内容资源。

好了,现在我们的工作就是让main方法执行起来不出错就OK了。

继续简化一下思路,insert和update操作,orm做的操作中心在:将Object class 转成对应的sql语句。

我们假设表名和类名一致,字段名和类成员变量名一致,那么,对于Document类,对应的db table为:

create table Document (id integer, title text, content text )

因此,我们采取java的reflect机制完成这个工作:
	@Override
	public void createTable(Class<? extends DbValObj> clazz) {
		
		StringBuffer sql = new StringBuffer();
		sql.append("create table ");
		
		String tabname = clazz.getSimpleName();
		sql.append(tabname);
		
		sql.append(" (");		
		
		Field[] fs = clazz.getDeclaredFields();
		for(Field f : fs) {
			
			String fname = f.getName();
			sql.append(fname).append(" ");
			
			Class fc = f.getType();
			if(fc == Integer.class) {
				sql.append(fname).append("integer, ");
			} else {
				sql.append(fname).append("text, ");
			}
		}
		
		sql.deleteCharAt(sql.lastIndexOf(","));
		sql.append(")");
		
		System.out.println(sql);
		
		try {
			update(sql.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

输出结果符合预期。

注意:简便起见,代码里面我们只判断了两种数据类型:int 和 string。

我们只是完成了sql语句的组织,由于我们并没有决定使用什么数据库,所以当前给出的方法:

update(sql.toString());

仅仅只是抛出异常:

	private void update(String sql) throws Exception {
		throw new RuntimeException("func update(String sql) is not implemented.");
	}

同理,我们搞定insert方法:

对应的语句是:

insert into Document (id, title, content) values (1, 'aaa', 'bbbbb')

	@Override
	public void insert(DbValObj valObj) {
		Class clazz = valObj.getClass();
		
		StringBuffer sql = new StringBuffer();
		sql.append("insert into ");
		
		String tabname = clazz.getSimpleName();
		sql.append(tabname);
		
		sql.append(" (");		
		List<Object> values = new ArrayList<Object>();
		Field[] fs = clazz.getDeclaredFields();
		for(Field f : fs) {
			
			f.setAccessible(true);
			String fname = f.getName();
			sql.append(fname).append(", ");
			
			Object value = "";
			try {
				value = f.get(valObj);
			} catch (Exception e) {
				e.printStackTrace();
			}								
			values.add(value);
		}
		
		sql.deleteCharAt(sql.lastIndexOf(","));
		sql.append(")");
		
		sql.append(" values (");
		for(Object val : values) {
			if(val instanceof Integer) {
				sql.append("").append(val).append(", ");	
			} else {
				sql.append("'").append(val).append("', ");
			}			
		}
		sql.deleteCharAt(sql.lastIndexOf(","));
		sql.append(")");
		
		System.out.println(sql);
		
		try {
			update(sql.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

最后是load方法,现在我们要换一个角度,load所做的事情,是把根据sql查询出来的结果进行bean的实例化和赋值。

这里我们同样先定义一个待底层实现的sql查询方法:

	private Map<String, String> select(String sql) throws Exception {
		throw new RuntimeException("func select(String sql) is not implemented.");
	}
这个方法,跟spring jdbctemplate 的 getMap 方法异曲同工:)然后是,实例赋值部分:

	@Override
	public DbValObj load(int id, Class<? extends DbValObj> clazz) {
		String tabname = clazz.getSimpleName();
		String sql = "select * from " + tabname + " where id=" + id;
		
		Map<String, String> row = null;
		try {
			row = select(sql);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
		DbValObj obj = null;
		try {
			obj = clazz.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
		Field[] fs = clazz.getDeclaredFields();
		for(Field f : fs) {
			f.setAccessible(true);
			
			String key = f.getName();
			String val = row.get(key);
			
			try {
				if(f.getType().getSimpleName() == "int") {
					f.set(obj, Integer.parseInt(val));
				} else {
					f.set(obj, val);
				}	
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return obj;
	}

Last ROUND!!!!

现在,让我们基于sqlite来做一个实现:)

package net.csdn.blog.deltatang.hibernate4me;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import SQLite.Callback;
import SQLite.Database;

public class SqliteDB extends SqlEngine {
	
	private Database database;

	public SqliteDB(String dbpath) {
		super();		
		
		File dbf = new File(dbpath);
		dbf.delete();
		
		try {
			this.database = new Database();
			
			database.open(dbpath, 0755);
			database.set_encoding("utf-8");
			
		} catch (Exception e) {
		}
	}
	
	private Map<String, String> select(String sql) throws Exception {

		final List<String> cols = new ArrayList<String>();
		final List<String> vals = new ArrayList<String>();
		
		database.exec(sql, new Callback() {
			@Override
			public void types(String[] arg0) {
			}
			@Override
			public boolean newrow(String[] arg0) {
				for(String val : arg0) {
					vals.add(val);
				}
				return false;
			}
			@Override
			public void columns(String[] arg0) {
				for(String val : arg0) {
					cols.add(val);
				}
			}
		});
		
		Map<String, String> res = new HashMap<String, String>(cols.size());
		
		for(int i = 0; i < cols.size(); i++) {
			res.put(cols.get(i), vals.get(i));
		}
		
		return res;
	}
	
	private void update(String sql) throws Exception {
		
		database.exec(sql, new Callback() {
			@Override
			public void types(String[] arg0) {
			}
			@Override
			public boolean newrow(String[] arg0) {
				return false;
			}
			@Override
			public void columns(String[] arg0) {
			}
		});
	}
	
	@Override
	public void createTable(Class<? extends DbValObj> clazz) { //............................
<pre name="code" class="java">//............................
 }@Overridepublic void insert(DbValObj valObj) {
//............................
 }@Overridepublic DbValObj load(int id, Class<? extends DbValObj> clazz) {
//............................
 }public static void main(String[] args) throws Exception {SqlEngine sdb = new SqliteDB("d:/docs.db");sdb.test();}}
 

RUN一下试试:

create table Document (id idtext, title titletext, content contenttext )
insert into Document (id, title, content ) values (1, '解放区的天是明朗的天', 'Document 1 的正文内容' )
.......................
insert into Document (id, title, content ) values (6, '人民万岁,人民万岁', 'Document 6 的正文内容' )
Document [id=1, title=解放区的天是明朗的天, content=Document 1 的正文内容]
.......................
Document [id=6, title=人民万岁,人民万岁, content=Document 6 的正文内容]

可以看到,我们成功的创建了表,然后插入了6项数据,然后根据ID 1.。。。6 逐一查询打印了出来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值