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 逐一查詢打印了出來。