您的位置:首页 > 其它

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

2014-07-28 15:00 465 查看
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 逐一查询打印了出来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: