【Hibernate】之模拟Hibernate持久化操作


 使用过Hibernate,大家都知道,由于其面向对象的设计,用起来非常方便,且具有很好的跨数据库性,那么Hibernate的底层是怎么实现的呢?其实也就是将对象模型转化为关系模型,最终还是得sql语句来执行。 

 看过Hibernate源码的同学应该发现, Hibernate底层的核心是代理和反射,那么由此这样我们就可以理解为什么使用Hibernate在效率上始终是致命的。

 

 Ok,下面是一个简单的模拟Hibernate-ORMsave()方法,来管中窥豹,略了解一下Hibernate的底层实现。其中这里我主要演示Hibernate是如何使用反射获取数据,类型等,到这里不得不提,广大Java从业者,想在Java技术道路上有所突破,Java的反射机制,是必须掌握的。

 

首先模拟出新建一个Session实体类,模拟一个save()方法

为了方便,我这边就不写配置文件了,如果各位想写,可以参考Hibernate


Configuration cfg=new Configuration();
cfg.configure();
sessionFactory=cfg.buildSessionFactory();

Hiberane的源码导进去,自己看一下它如何利用dom4j解析实现的。


首先创建一个实体类:

package csg.hibernate.entity;
/**
 * 
 * @author Caesar.hu
 * @Date	2014-11-28
 * @Time	上午09:32:08
 * @TODO
 */
public class Student {
	private Integer id;
	private String name;
	private Integer age;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	

}


看代码:然后我们模拟一个Session类,里面构造一个save()方法,

下面这段代码,就是核心,每行代码的注释已经写清楚

package csg.hibernate.entity;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author Caesar.hu
 * @Date 2014-11-27
 * @Time 下午06:02:05
 * @TODO模拟一个Session
 */
public class Session {
	// 1、写好一个表名,这个表名,理论上应该在Student.hbm.xml中读出来,或者是注解JPA映射
	// 我这里直接写出来,意思就是说,数据存到数据库的表对应的就是这个
	String tableName = "_student";
	// 2、为什么创建一个String类型的Map?
	// 这个map存放的就是实体的字段属性和数据库字段匹配,本身也是配置在文件中
	Map<String, String> cfs = new HashMap<String, String>();
	String[] methodNames;// new一个空集合主要是方便反射使用

	public Session() {
		cfs.put("_id", "id");
		cfs.put("_name", "name");
		cfs.put("_age", "age");
		methodNames = new String[cfs.size()];
	}

	public void save(Student s) throws Exception {
		// 3、创建SQL语句
		String sql = createSQL();
		String url = "jdbc:mysql://localhost/hibernate";
		String username = "root";
		String password = "root";
		Class.forName("com.mysql.jdbc.Driver");
		Connection conn = DriverManager.getConnection(url, username, password);
		PreparedStatement ps = conn.prepareStatement(sql);
		// 9,sql写好之后,这里是是不是需要设置?
		// 类似ps.setName(1,s.getId);
		// ps.setInterge(2,s.getAge)
		//怎么设置?你怎么知道传进来的是什么类型呢?所以这里最重要的是需要用到反射类型
		for (int i = 0; i < methodNames.length; i++) {
			System.out.println(methodNames[i]);// 首先取到存进到数据中的getAge getInt
			//10、 通过反射机制根据实体的方法反射出实体一系列的方法getAge,getName,getId和返回类型Integer,String,Integer
			Method m = s.getClass().getMethod(methodNames[i]);
			System.out.println(m);
			System.out.println(s.getClass() + "-------" + m + "---------"
					+ m.getName() + "-----" + m.getReturnType());
			//11、获取数组里面getAge,getInt,getName 的返回类型
			Class r = m.getReturnType();
			//12、根据返回类型判断其应该ps.setString,还是ps.setInteger
			if (r.getName().equals("java.lang.String")) {
				//13、invoke是反射里面的一个方法,其作用是通过类的返回值类型反射出属性值
				//14、getAge.invoke(s);同样也可以通过值反射出返回类型
				String returnValue = (String) m.invoke(s);
				System.out.println(returnValue);
				ps.setString(i + 1, returnValue);
			}
			//15、同样如果判断是Integer类型,就ps.setInteger
			if (r.getName().equals("java.lang.Integer")) {
				System.out.println(m.invoke(s));
				Integer returnValue = (Integer) m.invoke(s);
				// System.out.println(returnValue);
				ps.setInt(i + 1, returnValue);
			}
		}
		// ps.executeUpdate();
		ps.close();
		conn.close();
	}

	private String createSQL() {
		String str1 = "";
		int index = 0;
		for (String s : cfs.keySet()) {
			// 4、通过Map中的key得到Value
			String v = cfs.get(s);
			// 5、根据get,set方法我们可以知道字段首字母是需要大写的,
			// 6、这段代码就是将取出来的value首字母大写加上get
			v = Character.toUpperCase(v.charAt(0)) + v.substring(1);
			// 7、这样new出来的空集合里面就放上了getId,getName,getAge
			methodNames[index] = "get" + v;
			str1 += s + ",";
			index++;
		}
		str1 = str1.substring(0, str1.length() - 1);
		String str2 = "";
		for (int i = 0; i < cfs.size(); i++) {
			str2 += "?,";
		}
		str2 = str2.substring(0, str2.length() - 1);
		String sql = "insert into " + tableName + "(" + str1 + ")" + "values("
				+ str2 + ")";
		// 8、这段sql==
		// insert into _table(id,name,age)values(?,?,?);
		return sql;
	}

}


最后测试代码:


package JunitTest;

import csg.hibernate.entity.Session;
import csg.hibernate.entity.Student;
/**
 * 
 * @author Caesar.hu
 * @Date	2014-11-28
 * @Time	上午09:32:15
 * @TODO
 */
public class Test {
	public static void main(String[] args) throws Exception{
		Student student=new Student();
		Session s=new Session();
		student.setId(1);
		student.setAge(12);
		student.setName("张三");
		s.save(student);
	}

}


Ok,到这里,大家应该能明白,为什么说HibernateJDBC的封装?尤其是在考虑web性能的问题上为什么Hibernate不能满足开发需求,反射机制对于Java是多么重要

其实Hibernate的源码写法也莫不于此,只不过其在底层进行大量的封装,同时为了性能Hibernate也有采用直接将数据生成二进制流进行操作。对于配置文件大家可以看Hibernate 源码是如何利用dom4j解析Hibernate.cfg.xml这个文件,写的尤其经典,Ok

   Java从业者,想在技术上突破,反射机制是必须掌握的!