Hibernate中O/R Mapping的简单模拟

*******************************************************************************************************************

@author lue

@e-mail luecsc@gmail.com

学习笔记,仅供参考,如有错误请联系

*******************************************************************************************************************

通常的系统设计我们会使用JDBC操作数据库,而业务处理和数据存取逻辑则混杂在一起。一般都是按照如下步骤进行:

1、建立数据库连接,获得 Connection 对象。

2、根据用户的输入组装查询 SQL 语句。

3、根据 SQL 语句建立Statement 对象 或者PreparedStatement 对象。

4、用 Connection 对象执行 SQL语句,获得结果集 ResultSet 对象。

5、然后一条一条读取结果集 ResultSet 对象中的数据。

6、根据读取到的数据,按特定的业务逻辑进行计算。

7、根据计算得到的结果再组装更新 SQL 语句。

8、再使用 Connection 对象执行更新 SQL 语句,以更新数据库中的数据。

7、最后依次关闭各个 Statement 对象和Connection 对象。

例如在数据库中有一张user表,有3个字段,现在要向user表中插入数据,按照上面的步骤,首先新建一个User类:

	//User.java
	package cn.luecc.hibernate.model;
	public class User {
		private int id;
		private String name;
		private int age;
		//getter and setter ...
	}

简单的封装一下对数据库的操作:

//DBUtil.java
package cn.luecc.hibernate.model;
//import ......  

public class DBUtil {  
	private static Connection conn = null ;  
	private static PreparedStatement ps = null ;  
	public static Connection getConnection() 
	{  
		try {  
			Class.forName("com.mysql.jdbc.Driver") ;  
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hibernate","root","chenxx") ;  

		} catch (ClassNotFoundException e) {  
			// TODO Auto-generated catch block  
			e.printStackTrace();  
		}catch (SQLException e) {  
			// TODO Auto-generated catch block  
			e.printStackTrace();  
		}         
		return conn ;  
	}  
	public static PreparedStatement createStatement(String sql)  
	{  
		try {  
			conn = getConnection() ;  
			ps = conn.prepareStatement(sql) ;  
		} catch (SQLException e) {  
			// TODO Auto-generated catch block  
			e.printStackTrace();  
		}  
		return ps ;  
	}  
	public static void close()  
	{  
		try {  
			ps.close() ;  
			conn.close() ;  
		} catch (Exception e) {  
			e.printStackTrace() ;  
		}  
	}  
	}

然后再往数据库中插入数据:

//JDBCTest.java
package cn.luecc.hibernate.model;

import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCTest {
	public static void main(String[] args) {
		//简单的模拟,这里直接new一个User插入数据库中
		User u = new User();
		u.setId(5);
		u.setName("s5");
		u.setAge(55);
		String sql = "insert into user (id,name,age) values(?,?,?)";
		PreparedStatement pstmt = null ; 
		pstmt = DBUtil.createStatement(sql) ;
		try {
			pstmt.setInt(1, u.getId());
			pstmt.setString(2, u.getName());
			pstmt.setInt(3, u.getAge());
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			DBUtil.close();
		}	
	}	
}

运行效果如下:

但是由上可看出代码逻辑非常复杂,这还不包括某条语句执行失败的处理逻辑。其中的业务处理逻辑和数据存取逻辑完全混杂在一块。而一个完整的系统要包含成千上万个这样重复的而又混杂的处理过程,假如要对其中某些业务逻辑或者一些相关联的业务流程做修改,要改动的代码量将不可想象。另一方面,假如要换数据库产品或者运行环境也可能是个不可能完成的任务。而用户的运行环境和要求却千差万别,我们不可能为每一个用户每一种运行环境设计一套一样的系统。

所以就要将一样的处理代码即业务逻辑和可能不一样的处理即数据存取逻辑分离开来,另一方面,关系型数据库中的数据基本都是以一行行的数据进行存取的,而程序运行却是一个个对象进行处理,而目前大部分数据库驱动技术(如ADO.NETJDBCODBC等等)均是以行集的结果集一条条进行处理的。所以为解决这一困难,就出现 ORM 这一个对象和数据之间映射技术。

下面用ORM简单的模拟Hibernate将关系型数据库中的记录映射为Java对象的过程,虽然不太实用,但是却很简单明了地说明了映射的过程。

首先建立一个测试类:

//UserTest.java
package cn.luecc.hibernate.model;
public class UserTest {
	public static void main(String[] args) {
		User s = new User();
		s.setId(4);
		s.setName("s4");
		s.setAge(44);	
	
		Session session = new Session();
		session.save(s);
	}
}

我们建立一个Session类,在Session类中我们假设已经将配置文件读入到我们的Session类中,并存放在一个Map中:

	//先假定数据表名称
	String tableName = "user";
	//保存数据库列名和类的属性名
	Map<String,String> map = new HashMap<String,String>();
	//类的属性方法名保存为字符串  
	String[] methodNames;
	public void save(User u) {
		PreparedStatement ps = null;
		map.put("id", "id");
		map.put("name", "name");
		map.put("age" , "age");

上面的map中存放的是从配置文件中读出的配置信息,map<key,value>中的key为数据表user中的字段,valueUser对象的属性。

    然后我们来拼一个sql语句,对应的就是JDBC中的:

    insert into user (id,name,age) values(?,?,?)

//用來存放字段的屬性
		String str1 = "" ;  
		//用來存放後面的配置符合"?"
		String str2 = "" ;  
		int index = 0 ;  
		for(String s : map.keySet()){  
			String v = map.get(s) ;  
		//构造get方法名称 :首先取出字段名稱v,然後取出v的首字母傳換成大寫 ,
			//最后再截取v中除首字母外的字符
			//例如:字段名为id,拼成的方法名称就是getId
			methodNames[index] = "get" + Character.toUpperCase(v.charAt(0)) + v.substring(1) ;  
			str1 += s + "," ;  
			index++ ;  
		}  		str1 = str1.substring(0,str1.length()-1) ;  
		str1 = str1.substring(0,str1.length()-1) ;  
		for(int i=0; i<map.size() ; i++){  
			str2 += "?," ;  
		}  
		str2 = str2.substring(0, str2.length()-1) ;  
		String sql = "insert into " + tableName + " (" + str1 + " ) values (" + str2 +")" ;  

		return sql;  
	} 

我们已经将sql语句拼好,然后就是往sql语句中设值了,对应的JDBC语句就是

    pstmt.setInt(1,u.getId());

    pstmt.setString(2,u.getName());

    pstmt.setInt(3,u.getAge());

要完成这个动作,最困难的就是pstmt.setXXX(i+1,u.getXXX)中我们怎么来获得这个”XXX”,当然取得get方法已经在上面的程序中演示,下面是利用Java反射机制来获得set方法:

for(int i=0; i<methodNames.length; i++){  
	//利用Method的反射机制获取类的方法  
	Method m = u.getClass().getMethod(methodNames[i]) ;  
	//获得方法的返回值类型  
	Class type = m.getReturnType() ;  
	//打印方法名称,类型等 
	System.out.println(m.getClass() + " || " + m.getName() +" || "+ m.getReturnType() );  
	//根据返回值类型匹配,设置PreparedStatement的值  
	if(type.getName().equals("java.lang.String")){  
		String returnValue = (String)m.invoke(u) ;  
		ps.setString(i+1,returnValue) ;  
	}  
	if(type.getName().equals("int")){  
		Integer returnValue = (Integer)m.invoke(u) ;  
		ps.setInt(i+1,returnValue) ;  
	}  
}

通过以上方法就完成了一个简单的ORM模拟,这里可以观察一下运行结果,跟通过JDBC完全一样:

以下为完整的Session代码:

package cn.luecc.hibernate.model;
import java.lang.reflect.Method;  
import java.sql.PreparedStatement;  
import java.util.HashMap;  
import java.util.Map;  

public class Session {  
	//先假定数据表名称  
	String tableName = "user" ;  
	//保存数据库列名和类的属性名  
	Map<String , String> map = new HashMap<String,String>() ;  
	//类的属性方法名保存为字符串  
	String[] methodNames ;   
	public void save(User u) {  
		PreparedStatement ps = null ;  
		map.put("id", "id") ;  
		map.put("name", "name");
		map.put("age" , "age") ;  
		methodNames = new String[map.size()] ;//根据map的大小创建属性方法的字符串的个数  
		String sql = createSQL() ;//创建SQl语句  
		try {  
			ps = DBUtil.createStatement(sql) ;  
			for(int i=0; i<methodNames.length; i++){  
				//利用Method的反射机制获取类的方法  
				Method m = u.getClass().getMethod(methodNames[i]) ;  
				//获得方法的返回值类型  
				Class type = m.getReturnType() ;  
				System.out.println(m.getClass() + " || " + m.getName() +" || "+ m.getReturnType() );  
				//根据返回值类型匹配,设置PreparedStatement的值  
				if(type.getName().equals("java.lang.String")){  
					String returnValue = (String)m.invoke(u) ;  
					ps.setString(i+1,returnValue) ;  
				}  
				if(type.getName().equals("int")){  
					Integer returnValue = (Integer)m.invoke(u) ;  
					ps.setInt(i+1,returnValue) ;  
				}  
			}  
			ps.executeUpdate() ;  
		} catch (Exception e) {  
			e.printStackTrace() ;  
		}  
	}  
	private String createSQL() {  
		//用來存放字段的屬性
		String str1 = "" ;  
		//用來存放後面的配置符合"?"
		String str2 = "" ;  
		int index = 0 ;  
		for(String s : map.keySet()){  
			String v = map.get(s) ;  
			//构造get方法名称 :首先取出字段名稱v,然後取出v的首字母傳換成大寫 ,
			//最后再截取v中除首字母外的字符
			//例如:字段名为id,拼成的方法名称就是getId
			methodNames[index] = "get" + Character.toUpperCase(v.charAt(0)) + v.substring(1) ;  
			str1 += s + "," ;  
			index++ ;  
		}  
		str1 = str1.substring(0,str1.length()-1) ;  
		for(int i=0; i<map.size() ; i++){  
			str2 += "?," ;  
		}  
		str2 = str2.substring(0, str2.length()-1) ;  
		String sql = "insert into " + tableName + " (" + str1 + " ) values (" + str2 +")" ;  
		return sql;  
	}  
}  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值