这两天在做山寨的 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; } }
还待完善
好了,不宜将所有代码都帖上,有兴趣就下代码看看吧