`
zuiyanwangyue
  • 浏览: 167541 次
  • 性别: Icon_minigender_1
  • 来自: 河北
社区版块
存档分类
最新评论

模拟Hibernate ORM映射机制

阅读更多

今天手动实现了一个小例子,模拟Hibernate将关系型数据库中的记录映射为Java对象的过程,虽然不太实用,但是却很简单明了地说明了映射的过程。下图是这个例子的类图:

我们首先对上面的类图做一个简要的说明,IEntity是一个所有的实体都要实现的接口,它的getDefinition方法返回的是这个实体的定义(EntityDefinition),save方法接受一个java.sql.Connection类型的参数,然后把实体自己保存到数据库中,为简单起见这里没有定义其它数据访问的方法。

EntityDefinition描述了一个实体映射到数据库中表的规则,为了简单起见我在这里设置的规则都是很简单的,它的tableName属性直接说明了它所属于的实体对应的数据库中的表名,实体与它本身的定义是一对一的关系,每一个实体都会持有它自己的定义。另外PropertyDefinition描述的是实体中的属性映射到数据库某个表中的字段的规则,EntityDefinition与它是一对多的关系,因为一个实体会有多个属性对应着数据库中表的多个字段。

AbstractEntity是IEntity的默认实现,此类的名字以Abstract开头是为了说明这个类是为了继承而设计的,那么继承它的子类可以得到什么好处呢?因为子类继承自一个父类就和认干爹差不多,若是没有好处谁情愿做人家的干儿子是不是?我们在此承诺,继承自AbstractEntity的子类不用自己编写任何持久化的代码就能实现将自己保存到数据库中的愿望,它所要做的就是像POJO那样声明一些属性,并在自己定义的包中建立一个如下格式的配置文件:类名.hbm.xml,例如对于Customer类来说,要建立一个Customer.hbm.xml的配置文件,文件的内容和上述类图中提供的类似。

先让我们看看IEntity的源码:

package com.neuqsoft.data.analyse.common;

import java.sql.Connection;
import java.sql.SQLException;

public interface IEntity {
	EntityDefinition getDefinition();
	
	void save(Connection conn)throws SQLException;
}

接口中描述的方法都很容易看懂,这里不加赘述,下面是AbstractEntity的源码:

package com.neuqsoft.data.analyse.common;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public abstract class AbstractEntity implements IEntity {
	private EntityDefinition entityDef;
	
	protected AbstractEntity(){
		entityDef=new EntityDefinition(getClass());
	}
	
	public final void save(Connection conn)throws SQLException{
		deleteIfExist(conn);//如果记录存在先删除
		doSave(conn);
	}
	private void deleteIfExist(Connection conn)throws SQLException{
		PropertyDefinition idDefinition=entityDef.getIdDefinition();
		String deleteSQL="delete from "+entityDef.getTableName()+" where "+idDefinition.getColumnName()+"=?";
		System.out.println("DELETE:"+deleteSQL);
		PreparedStatement ps=conn.prepareStatement(deleteSQL);
		if(ps!=null){
			setValue(ps,1,idDefinition);
			ps.executeUpdate();
			ps.close();
		}
	}
	
	private void setValue(PreparedStatement ps, int index, PropertyDefinition pd)
		throws SQLException{
		int type=pd.getType();
		Object value=getValue(pd.getName());
		if(type==PropertyDefinition.TYPE_STRING) ps.setString(index,value.toString());
		else if(type==PropertyDefinition.TYPE_NUMBER) ps.setBigDecimal(index,(BigDecimal)value);
		else if(type==PropertyDefinition.TYPE_DATE) ps.setDate(index,(Date)value);
	}

	private Object getValue(String name) {
		try{
			Field f=getClass().getDeclaredField(name);
			f.setAccessible(true);
			return f.get(this);
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private final void doSave(Connection conn)throws SQLException{
		PropertyDefinition[] pds=entityDef.getAllPropertyDefinitions();
		StringBuffer sb=new StringBuffer("insert into "+entityDef.getTableName()+"(");
		StringBuffer valueBuffer=new StringBuffer("values(");
		for(int i=0;i<pds.length;i++){
			sb.append(pds[i].getColumnName());
			valueBuffer.append("?");
			if(i<pds.length-1){
				sb.append(",");
				valueBuffer.append(",");
			}
		}
		String insertSQL=sb.toString()+")"+valueBuffer.toString()+")";
		System.out.println("INSERT:"+insertSQL);
		PreparedStatement ps=conn.prepareStatement(insertSQL);
		if(ps!=null){
			for(int i=0;i<pds.length;i++) setValue(ps,i+1,pds[i]);
			ps.executeUpdate();
			ps.close();
		}
	}
	
	public EntityDefinition getDefinition() {
		return entityDef;
	}
}

为了实现save(Connection conn)方法,AbstractEntity类定义了几个私有方法,在此做简要说明:

(1)deleteIfExist(Connection conn)throws SQLException 如果实体已经在数据库中存在则首先删除,然后保存,相当于更新操作,判断是否存在的依据是实体的ID,如果实体在数据库中不存在此方法相当于零操作。

(2)setValue(PreparedStatement ps, int index, PropertyDefinition pd) 为PreparedStatement设置参数,目前PropertyDefinition所支持的参数只有三种:字符串String类型,日期Date类型以及数字BigDicemal类型。我们当然可以让它支持更多的类型,这里只支持三种类型只是为了简化起见。

(3)Object getValue(String name) 使用反射机制获取实体某一个属性所对应的属性值,name是声明的属性名,例如Customer类中所声明的private String name。

(4)doSave(Connection conn)throws SQLException 真正将实体保存到数据库中的操作。

AbstractEntity类的构造方法虽然只有一行语句:entityDef=new EntityDefinition(getClass());却是整个系统的关键,因为它初始化了实体的定义,下面让我们看看EntityDefinition的源码:

package com.neuqsoft.data.analyse.common;

import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class EntityDefinition{
	private String tableName;//实体对应的表
	public static final String CONFIG_SUFFIX=".hbm.xml";
	//<实体属性,实体属性的定义>
	private Map propertyDefMap=new HashMap();
	//
	public EntityDefinition(Class clazz) {
		String configFileName=clazz.getSimpleName()+CONFIG_SUFFIX;
		URL url=clazz.getResource(configFileName);
		try{
			Document document=new SAXReader().read(url.openStream());
			Element root=document.getRootElement();
			tableName=root.attribute("tableName").getValue();
			//mapping property to column of table
			Iterator propElements=root.elementIterator("Property");
			while(propElements.hasNext()){
				Element element=(Element) propElements.next();
				PropertyDefinition pd=new PropertyDefinition(element);
				propertyDefMap.put(pd.getName(), pd);
			}
		}catch(Exception e){
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public PropertyDefinition getIdDefinition(){
		Iterator it=propertyDefMap.values().iterator();
		while(it.hasNext()){
			PropertyDefinition pd=(PropertyDefinition)it.next();
			if(pd.isId()) return pd;
		}
		return null;
	}
	
	public PropertyDefinition[] getAllPropertyDefinitions(){
		Collection cl=propertyDefMap.values();
		return (PropertyDefinition[]) cl.toArray(new PropertyDefinition[cl.size()]);
	}
	
	public String getTableName() {
		return tableName;
	}
}

显而易见,EntityDefinition是通过获取类似Customer.hbm.xml的配置文件来初始化自己的,它的PropertyDefinition getIdDefinition()方法返回其所属于的实体的ID的PropertyDefinition实例,这里除了isId()方法返回true外,和其它属性的定义没有区别。这里假定每一个实体的配置文件都会指定一个ID,如下所示:<Property name="id" columnName="ID" type="STRING"  isId="true"/>

可以看出EntityDefinition维护着一个Map,这个Map是由实体的属性名映射到其对应的PropertyDefinition(属性定义)。下面是PropertyDefinition的源码:

package com.neuqsoft.data.analyse.common;

import org.dom4j.Element;

public class PropertyDefinition{
	public static final int TYPE_UNKNOWN=-1;
	public static final int TYPE_STRING=0;
	public static final int TYPE_NUMBER=1;
	public static final int TYPE_DATE=2;
	private String name;//实体中的属性名
	private String columnName;//实体相应属性对应表中的列名
	private int type;//columnName在表中定义的类型
	private boolean id;//是否是标识符
	public PropertyDefinition(String name, String columnName, int type) {
		this.name = name;
		this.columnName = columnName;
		this.type = type;
	}
	public PropertyDefinition(String name, String columnName, String type){
		this(name,columnName,analyseType(type));
	}
	
	public PropertyDefinition(Element el){
		setName(el.attributeValue("name"));
		setColumnName(el.attributeValue("columnName"));
		setType(analyseType(el.attributeValue("type")));
		String isIdString=el.attributeValue("isId");
		boolean isId=(isIdString==null?false:Boolean.valueOf(isIdString).booleanValue());
		setId(isId);
	}
	
	public String getColumnName() {
		return columnName;
	}
	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	
	private static int analyseType(String type) {
		if("STRING".equalsIgnoreCase(type)) return TYPE_STRING;
		else if("NUMBER".equalsIgnoreCase(type)) return TYPE_NUMBER;
		else if("DATE".equalsIgnoreCase(type)) return TYPE_DATE;
		else return TYPE_UNKNOWN;//Unknown type
	}
	public boolean isId() {
		return id;
	}
	public void setId(boolean id) {
		this.id = id;
	}
}

下面是用于测试的Customer.java以及Customer.hbm.xml的源码:

import java.math.BigDecimal;

import com.neuqsoft.data.analyse.common.AbstractEntity;

public class Customer extends AbstractEntity {
	private String id;
	private String name;
	private BigDecimal age;
	public BigDecimal getAge() {
		return age;
	}
	public void setAge(BigDecimal age) {
		this.age = age;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
<Entity tableName="CUSTOMER">
        <Property name="id" columnName="ID" type="STRING"  isId="true"/>
        <Property name="name" columnName="NAME" type="STRING"/>
        <Property name="age" columnName="AGE" type="NUMBER"/>
</Entity>

  The End.

  • 大小: 84.6 KB
分享到:
评论

相关推荐

    利用java反射、注解及泛型模拟ORM实现

    这篇博文“利用java反射、注解及泛型模拟ORM实现”旨在探讨如何结合这三种技术来实现对象关系映射(ORM),这是一种将数据库表与Java对象之间进行绑定的技术,简化了数据操作。 首先,我们来理解一下这三个概念: ...

    Hibernate实现原理模拟

    本教程将通过模拟Hibernate的实现原理,帮助开发者深入理解其工作方式,以便更好地运用到实际项目中。 **一、Hibernate概述** Hibernate是一个开源的ORM框架,它提供了一种在Java应用中持久化对象到关系数据库的...

    java模拟hibernate实现

    本项目通过Java的反射和注解技术,试图模拟Hibernate的部分功能,以便更好地理解和掌握ORM的核心原理。 1. **Java反射**: 反射是Java提供的一种强大的动态类型特性,允许程序在运行时获取类的信息(如类名、属性、...

    Hibernate模拟

    在Java开发中,Hibernate是一个非常重要的对象关系映射(ORM)框架,它极大地简化了数据库操作。本项目旨在通过模拟实现,帮助开发者深入理解Hibernate的工作原理,以及如何在实际项目中有效利用其功能。 **一、...

    模拟hibernate源代码

    在本文中,我们将深入探讨通过模拟Hibernate源代码来理解其工作原理和核心功能。 首先,Hibernate的核心思想是将Java对象与数据库表进行映射,这样开发者可以使用面向对象的方式来处理数据库操作,而无需编写繁琐的...

    反射+注解自定义ORM

    自定义ORM意味着开发者根据自己的需求创建一套框架,代替现有的ORM解决方案(如Hibernate、MyBatis)。这样做可以更贴近项目的特定需求,提高效率,同时减少不必要的复杂性。实现自定义ORM的关键步骤包括设计数据...

    控制反转应用,模拟Hibernate

    Hibernate是一个流行的对象关系映射(ORM)框架,它在处理数据库操作时,利用IoC来管理对象的生命周期和依赖关系。通过IoC,开发者可以专注于业务逻辑,而无需关心数据访问层的具体实现。 描述中提到的“读取XML,...

    模拟hibernate根据表生成Java bean文件

    本话题聚焦于如何模拟Hibernate的功能,自动生成Java Bean文件,这在开发过程中可以极大地提高效率,尤其是在处理大量数据库表结构时。以下是关于这个主题的详细知识: 1. **Hibernate简介**: Hibernate是一个...

    Hibernate类型映射

    Hibernate是Java领域中一款流行的持久化框架,它简化了对象关系映射(ORM)的过程,使得开发者能够以面向对象的方式处理数据库操作。在Hibernate中,类型映射是至关重要的概念,因为它负责将Java对象的属性与数据库...

    模拟hibernate的session.save()功能

    在这个"模拟hibernate的session.save()功能"的资源中,我们将深入理解Hibernate的核心操作之一:持久化对象。此程序源码的目的是帮助我们理解如何在没有Hibernate库的情况下实现类似的功能,这有助于我们更好地掌握...

    模拟hibernate注解功能

    在这个名为“模拟hibernate注解功能”的项目中,我们将探讨如何利用自定义注解和反射技术来模仿Hibernate的注解功能。 首先,让我们理解一下Hibernate的注解。Hibernate支持JPA(Java Persistence API)标准,其中...

    模拟hibernate中一级缓存

    在提供的"模拟hibernate中一级缓存"的案例中,我们可以创建一个简单的实体类,例如`User`,并使用Hibernate的Session接口进行操作。首先,通过`Session.get()`方法加载一个User对象,然后关闭Session。接着,再次...

    EasyOrm:这是一个模拟Hibernate和MyBatis的简单orm框架

    7. **缓存机制**:虽然EasyOrm可能没有像Hibernate那样全面的缓存机制,但可以根据实际需求考虑集成第三方缓存库,如Redis或Memcached,提高数据访问速度。 EasyOrm作为一个轻量级ORM框架,它的设计目标是简化开发...

    JavaEE技术-自主学习.zip_hibernate_hibernate session _javaee映射.xml

    然后,我们模拟Hibernate的Session接口,这是操作数据库的主要入口。Session提供了保存、删除、更新和查询对象的方法。例如: ```java SessionFactory sessionFactory = Configuration().configure()....

    Hibernate原理解析

    Hibernate是一个开源的Java语言下的对象关系映射(ORM)框架,它为开发者提供了在Java应用程序中操作数据库的强大工具。通过Hibernate,开发者可以将数据库操作与业务逻辑解耦,使得代码更加面向对象,提高了开发...

    Hibernate一对多主键关联映射源代码

    在Java的持久化框架Hibernate中,一对多关系(OneToMany)是一种常见的对象关系映射(ORM)映射方式,它模拟了数据库中一个表的一条记录与另一表的多条记录之间的关联。本教程通过源代码的形式,讲解如何在Hibernate...

    Hibernate一对多映射

    在Java的持久化框架Hibernate中,一对多映射是一种常见的对象关系映射(ORM)配置方式,用于模拟数据库中的关联关系。在这个场景中,一个实体(类)可以拥有多个其他实体实例,就像在数据库中一个表的一条记录可以...

    hibernate注解功能模拟

    在Java编程中,这三个概念对于理解对象关系映射(ORM)框架,尤其是Hibernate的工作机制至关重要。 Hibernate是一个流行的关系型数据库管理系统(RDBMS)到Java对象的映射工具,它极大地简化了数据访问层的开发。...

    struts2整合hibernate的网上银行模拟项目

    Struts2 主要负责MVC(模型-视图-控制器)架构的实现,提供了一种组织应用程序逻辑的方式,而Hibernate 是一个对象关系映射(ORM)工具,用于简化数据库操作。这个“struts2整合hibernate的网上银行模拟项目”结合了...

    自定义Orm框架的实现

    本项目旨在实现一个基于JDK5.0注解的小型ORM框架,模拟Hibernate的部分功能。 首先,我们要理解ORM的基本原理。ORM的核心思想是将数据库中的表映射为Java对象,将表中的记录映射为对象的实例,这样就可以通过操作...

Global site tag (gtag.js) - Google Analytics