这两天在做山寨的 hibernate,现已完成了 Session save,update, delete, get 还有Query 接口的实现.
Hiberate ORM是软件在对象与关系数据库之间进行转换的一个模块层, 可简化数据库操作Dao类的实现.
一. 技术要点:
1. 动态装载
程序运行后才决定要装载哪些类,然后才实例化.
装载
Class c = Class.forName(className);
实例化
Object o = c.newInstance(); //调用的只是类的无参构造方法,但对本程序来说已经足够了
2 .反射
通过一个类的Class 类对象可以得到此类的所有信息,如 静态块,属性,方法,父类、接口,构造器,异常,方法参数,方法返回值, 它们都以对象的形式出现。一切皆对象。但是private 修饰的属性,方法却得不到,还是得要访问权限的。
java.lang.reflect Method[] m = c.getMethods();//到所有方法
...
得到的方法还可以调用
如 Object rt = m[0].invoke(o,参数);
class User{
private String name;
public void getName()
{
return name
}
public String setName(String name){
this.name = name;
}
}
如果我们要把从数据库中得到的 "name" 字段值 赋给 User 的name属性该怎么做?
那就应该调用 setName方法了.不会是一般的调用,用反射。
3。读取xml配置文件
使用dom4j
SAXReader reader = new SAXReader();
Document doc = reader.read(file); // file为File 对象
Element root = doc.getRootElement(); //得到根结点
Element e = root;
Iterator it=e.elementIterator();//得到迭代器
4. 得到xml配置文件的路径
这我以前讲过的,在写web工程时 src目录下的文件实际上被放到了 WEB-INF/classes下了
String path = this.getClass.toString();//得到类名
path = this.getClass.getResource(path.substring(path.lastIndexOf(".")+1)+".class").toString();//得得此类的路径
path = path.substring(0,path.indexOf("/WEB-INF")); //得到项目路径
path = path + "/WEB-INF/classes";//得到classes路径
path = path + "/hibernate.hbm.xml";//得到配置文件路径
if(path.startsWith("jar:")){
path = path.substring(4);
}
URI uri = null;
try {
uri = new URI(path);
} catch (URISyntaxException e) {
e.printStackTrace();
}
Log.printLog("uri="+uri);
File file = new File(uri);
if(!file.exists()){
Log.printLog("配置文件不存在!");
return null;
}
二。 配置及使用
1. 在src目录下建立hibernate.hbm.xml,先要引入mysql的驱动包啊
<?xml version="1.0" encoding="UTF-8"?> <hibernate-configuration> <session-factory> <property name="show_sql">true</property> //显示sql <property name="dialect">mysql</property> //数据库名 <property name="driver">com.mysql.jdbc.Driver</property> // <property name="url">jdbc:mysql://localhost:3306/catpage</property> <property name="username">root</property> <property name="password">root</property> <mapping-resource>User.hbm.xml</mapping-resource> //pojo映射文件 </session-factory> </hibernate-configuration>
这和真的Hernate 差不多
2. 再在同级目录下建User.hbm.xml文件,也可以要其他路径,但得修改hibernate.hbm.xml了
<?xml version="1.0" encoding="UTF-8"?> <hibernate-mapping> <class name="cn.netjava.model.User" table="user"> //pojo路径及表名 <id name="id" column="id"></id> //主键 <property name="name" column="name" /> //字段 <property name="pwd" column="pwd"/> </class> </hibernate-mapping>
3.编写pojo类 如User
package cn.netjava.model;
public class User {
private int id;
private String name;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
4.开始测试吧
package cn.netjava.model;
import cn.netjava.hibernate.Configuration;
import cn.netjava.hibernate.Session;
public class Test {
public static void main(String args[]){
try {
//
Session session = new Configuration().configure().buildSessionFactory().openSession();
User user = (User)session.query(User.class, 8);//取得id为8的user
System.out.println("name="+user.getName()+"\tpwd="+user.getPwd());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
打印结果:
//省略.事先已经有了user表,也可用save方法,就可不事先建表啦
name=a pwd=a
三..再看代码吧.
首先是Session接口
package cn.netjava.hibernate;
public interface Session {
/**
* 保存 pojo到数据库中
* @param obj pojo bean对象
* @return 返回主键
*/
public int save (Object obj,Class c) ;
/**
* 查询数据库得到pojo对象
* @param c pojo的类对象
* @param priKey 主键
* @return pojo对象
*/
public Object query(Class c,Object priKey);
/**
* 更新pojo到数据库中
* @param priKey 主键 属性值
* @param obj 要更新的pojo对象
* @param c 要更新的pojo 类对象
*/
public void update(Object priKey,Object obj,Class c);
/**
* 从数据库中删除 pojo
* @param priKey 主键值
* @c: pojo的类对象
* return 删除是否成功
*/
public boolean delete(Object priKey,Class c);
/**
* 使用sql查询语句查询
* @param sql sql查询语句
* @return 查询对象
*/
public Query createQuery(String sql);
}
它的实现类是 HibernateSession, 只看save代码好了
/**
* 保存 pojo到数据库中
* @param obj pojo bean对象
* @c Pojo的类对象
* @return 返回主键
*/
public int save (Object obj,Class c) {
MappingConfiguration mc = findConfig(c);//pojo的配置信息类
// 查询是否有表存在,不存在则创建
if(!hasTable(mc.getTable())){
try {
if(createTable(obj,c,mc.getTable())){
Log.printLog("已创建表"+mc.getTable());
tableMap.put(mc.getTable(), true);
}
else{
Log.printLog("创建表失败");
return 0;
}
} catch (Exception e) {
Log.printLog("创建表失败");
e.printStackTrace();
return 0;
}
}
List<Word> wordList = mc.getWordList(); // 取得pojo的字段列表
//生成sql语句
String sql = "insert into "+mc.getTable() +"(";
for(Word w:wordList){
if(!w.getAttrname().equals(mc.getPriKey().getAttrname())){//不是主建
sql+=w.getColumn()+",";
}
}
sql+=") values(";
for(Word w:wordList){
if(!w.getColumn().equals(mc.getPriKey().getColumn())){
sql+="?,";
}
}
sql+=")";
/* 下几行为将sql中()里的多余的','去掉*/
StringBuffer sb = new StringBuffer(sql);
int s = sb.indexOf(",)");
while(s>0){
sb.deleteCharAt(s);
s=sb.indexOf(",)");
}
sql =sb.toString();
PreparedStatement pstmt = dbconn.getPSatement(sql);
int num=0;
try{
//填充sql中的?
for(Word w:wordList){
if(!w.getColumn().equals(mc.getPriKey().getColumn())){
Method m = Tool.getGetMethod(c,w.getAttrname());
Object ort = m.invoke(obj);
System.out.println("ort="+ort);
pstmt.setString(++num, ort.toString());
}
}
pstmt.execute();
}
catch(Exception e){
e.printStackTrace();
}
//以下为得到表中最后一行,因为设定mysql的主键生成方式为递增的,才能得到刚插入的行的 id
sql = "select * from "+mc.getTable()+" order by "+mc.getPriKey().getColumn()+" desc limit 1";
Query query = createQuery(sql);
query.addEntity(c);
List l = query.list();
Object result = l.get(0);
Method mPriKey = Tool.getGetMethod(c, mc.getPriKey().getAttrname());//取得主键的get方法
Object rt = null;
try {
rt = mPriKey.invoke(result);
} catch (Exception e) {
Log.printLog("取得主键时出错了");
e.printStackTrace();
}
Log.printLog("priKek="+rt);
return Integer.parseInt(rt.toString());
}
再看Query接口吧,它是查询数据库必要的,它的实现 是QueryImp,不贴出来了
package cn.netjava.hibernate;
import java.util.List;
/**
* 查询接口
* @author sky
*
*/
public interface Query {
/**
* 给查询得到的结果加入类型
* @param c 要转换成的类的类对象
*/
public void addEntity(Class c);
/**
* 执行查询
* @return 是否成功
*/
public boolean excute();
/**
* 得到查询结果,放到List中
* @return 结果列表
*/
public List list();
/**
* 设置预编译sql
* @param index '?'的索引,从1开始
* @param value '?'的值
*/
public void setString(int index,String value);
/**
* 设置预编译sql
* @param index '?'的索引,从1开始
* @param value '?'的值
*/
public void setInt(int index,int value);
/**
* 设置预编译sql
* @param index '?'的索引,从1开始
* @param value '?'的值
*/
public void setFloat(int index,float value);
}
Configuration配置类
/**
* hibernate 的配置类
* @author sky
*
*/
public class Configuration {
private boolean isShowSql; //是否显示sql语句
private String dialect; //连结数据库的类型
private String driver; //连结数据库的驱动
private String url; //连结数据库的url地址
private String username; //连结数据库的用户名
private String password; //连接数据库的密码
private List<String> mapping_resource; //pojo的映射配置文件
private List<MappingConfiguration> mcList; //pojo的映射配置信息列表
/*
上面所有属性的seter,getter方法都有
*/
/**
* 读取配置文件
* @return this
*/
public Configuration configure(){
/*读xml*/
......
}
/**
* 建立Session的工厂类
* @return 工厂
* @throws Exception
*/
public SessionFactory buildSessionFactory() throws Exception{
SessionFactory sf = SessionFactory.getSessionFactory(this);
return sf;
}
}
还待完善
好了,不宜将所有代码都帖上,有兴趣就下代码看看吧