拿起筆來做刀槍 · 之六 再造一個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 逐一查詢打印了出來。

發佈了94 篇原創文章 · 獲贊 6 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章