【JavaSE】Hibernate框架的简单实现(工具思想)

Hibernate的优点:
1、对象化。hibernate可以让开发人员以面相对象的思想来操作数据库。jdbc只能通过SQL语句将元数据传送给数据库,进行数据操作。而hibernate可以在底层对元数据和对象进行转化,使得开发者只用面向对象的方式来存取数据即可。
2、更好的移植性。hibernate使用xml或JPA的配置以及数据库方言等等的机制,使得hibernate具有更好的移植性,对于不同的数据库,开发者只需要使用相同的数据操作即可,无需关心数据库之间的差异。而直接使用JDBC就不得不考虑数据库差异的问题。
3、开发效率高。hibernate提供了大量的封装(这也是它最大的缺点),很多数据操作以及关联关系等都被封装的很好,开发者不需写大量的sql语句,这就极大的提高了开发者的开发效率。
4、缓存机制的使用。hibernate提供了缓存机制(session缓存,二级缓存,查询缓存),对于那些改动不大且经常使用的数据,可以将它们放到缓存中,不必在每次使用时都去查询数据库,缓存机制对提升性能大有裨益。
————————————————
版权声明:本文为CSDN博主「nwpu_geeker」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/nwpu_geeker/article/details/79029529

上述是我在某博客上看到的Hibernate框架的优点,下面是我自行实现的一个简易的Hibernate框架。

首先,我先明确我做这个工具的目的:我希望通过输入 MecDatabase database = new MecDatabase(); database.list(StudentInfo.class); 这样调用,就能够得到数据库表mec_student_info中的所有记录。

在观察了数据库的表和字段以及类的类和成员之后,发先了他们之间的一种很熟悉关系——map;
我们可以将类和数据库的表对应起来,将类的成员和表的字段对应起来,显而易见,这是两个map;

先做好我们的准备工作:先打开一个数据库的表,再编写一个类;
在这里插入图片描述

/**
 * 一个普通的类,只是增加了一些get和set方法,还覆盖了一下toString方法,和equals方法
 * @author 虾米
 *
 */
public class NativeInfo {
	private String id;
	private String columnName;   //故意设置成 和数据库表中的字段名字不一样的名字
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getColumnName() {
		return columnName;
	}
	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	
	/**
	 * 这里的相等比较,是通过比较 id的值;
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		NativeInfo other = (NativeInfo) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
	
	@Override
	public String toString() {
		return "NativeInfo [id=" + id + ", columnName=" + columnName + "]";
	}
	
}

通过名字可以看出,上述的表和类是有对应关系的。
编写一个XML文件,和一个properties文件,再在这个两个文件里进行配置,
在这里插入图片描述在这里插入图片描述
上述实我的配置文件。第一个XML文件里配置了:类的名称、表的名称、成员名称、字段名称;(注:成员名称和字段名称仅仅是指:在类和表中,名字不能对应的成员和字段,比如上面写到的:name 和 columnName)

于是,先编写第一个类:TableClassDefinition类(里面主要内容是定义了一个Class 成员,用来表示一个类,一个String成员,还有一个map,具体方法的作用等等,我都已经注释)

/**
 * 这个类表示:一个类和一张表相对应
 * @author 虾米
 */
public class TableClassDefinition {
	private Class<?> klass;
	private String table;
	//这里用map而不用list的原因是 :
	//我需要对这个map进行遍历,而list的时间复杂度太高,(为什么需要遍历:因为在我所给的例子中,我数据库的字段是id和name,
	//而我的NativeInfo类中是id和columnName,其中name和columnName不一样。
	//我先假设所有字段的名字和我的类的成员的名字都对应一样,先把我的Property类中的property和colunm设置成一样的名字,
	//我在我的xml配置文件里,再增加一个column属性,
	//这个属性专门表示我的NativeInfo类中与数据库中字段名字不一致的成员,在TableClassDefinition类中专门定义一个setColumn方法来设置,
	//把不一样的设置成一样的名字)
	//用类的成员的名字作为键,用描述类的成员和字段的对应关系的类作为值
	private Map<String , Property> propertiesMap;
	
	public TableClassDefinition() {
	}

	public void setKlass(String klassName) {
		try {
			this.klass = Class.forName(klassName);
			propertiesMap = new HashMap<String, Property>();
			Field[] fields = klass.getDeclaredFields();
			
			for (Field field : fields) {
				String columnName = field.getName();
				Property property = new Property();
				//这里如上述所述,把column设置成property所对应的名字(也就是两个成员的“名字一样”)
				property.setProperty(field);
				property.setColumn(columnName);
				propertiesMap.put(columnName, property);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public List<Property> columnNameList() {
		List<Property> list =  new ArrayList<Property>();
		
		for (String key : propertiesMap.keySet()) {
			list.add(propertiesMap.get(key));
		}
		return list;
	}
	
	//这个方法可以得到一个表的所有字段,用它来和MecDatabase类中的str共同形成一个sql语句!!!
	public String columnList() {
		StringBuilder str = new StringBuilder();
		
		boolean first = false;
		for (String key: propertiesMap.keySet()) {
			str.append(first ? "," : " ");
			str.append(table).append(".").append(propertiesMap.get(key).getColumn());
			first = true;
		}
		
		return str.toString();
	}
	
	//这个 方法是XML配置文件里面第二个属性的内容,也就是将所列出的字段名称和成员名称不相同的字段的名字取出来,
	//对原来的propertiesMap进行设置;
	public void setColumn(String propertyName, String column) {
		Property property = propertiesMap.get(propertyName);
		if (property == null) {
			return ;
		}
		property.setColumn(column);
	}

	public String getTable() {
		return table;
	}

	public void setTable(String table) {
		this.table = table;
	}

	public Class<?> getKlass() {
		return klass;
	}
 
}

编写第二个类:Property

/**
 * 这个类表示:类的一个成员和表的一个字段相对应
 * @author 虾米
 */
public class Property {
	private Field property;
	private String column;
	
	public Property() {
	}

	public String getPropertyName() {
		return property.getName();
	}
	
	public Class<?> getType() {
		return property.getType();
	}
	
	public Field getProperty() {
		return property;
	}

	public void setProperty(Field property) {
		this.property = property;
	}

	public String getColumn() {
		return column;
	}

	public void setColumn(String column) {
		this.column = column;
	}
	
}

编写第三个类:TableClassFactory

/**
 * 这类包含一个XML文件中所有的类和表,把他们放在一个map里面
 * @author 虾米
 */
public class TableClassFactory {
	//这里的String 指的是类的名字;
	private static final Map<String , TableClassDefinition> tableClassPool;
	
	static {
		tableClassPool = new HashMap<String, TableClassDefinition>();
	}
	
	public TableClassFactory() {
	}
	
	//这个方法,是通过XML解析工具,将一个xml文件里面的内容进行解析,
	public static void scanTableClassMapping(String xmlPath) {
		new XMLParser() {
			//这里是把类的名字和数据库中的表的名字解析出来放在一个TableClassDefinition类中,并将解析出来的类的名字作为键,
			//这个TableClassDefinition类作为值,添加到tableClassPool中
			@Override
			public void dealElement(Element element, int index) {
				String klass = element.getAttribute("class");
				String table = element.getAttribute("table");
				TableClassDefinition tcb = new TableClassDefinition();
				
				tcb.setTable(table);
				tcb.setKlass(klass);

				new XMLParser() {
					//这里是继续解析XML文件的column属性,目的是把类的成员名字和表的字段名字不相同的,解析出来,在进行设置
					//因为我们在一开始是假设类的成员名字和表的字段名字是相同的,这里通过set方法把不同的名字进行设置。
					@Override
					public void dealElement(Element element, int index) {
						String property = element.getAttribute("property");
						String name = element.getAttribute("name");
				
						tcb.setColumn(property, name);
					}
				}.parseTag(element, "column");
				
				tableClassPool.put(klass, tcb);
			}
		}.parseTag(XMLParser.getDocument(xmlPath), "mapping");
	}
	
	public static TableClassDefinition getTableClass(String klass) {
		return tableClassPool.get(klass);
	}
	//这里是方法的重载;
	public static TableClassDefinition getTableClass(Class<?> klass) {
		return getTableClass(klass.getName());
	}
}

编写第四个类:MecDatabase

/**
 * 
 * MecDatabase database = new MecDatabase();
 *	database.list(StudentInfo.class);   	这样调用,就能够得到数据库表mec_student_info中的所有记录
 * @author 虾米
 *
 */
public class MecDatabase {
	private static Connection connection;
	
	public MecDatabase() {
	}
	
	//通过自己写的Properties工具,把properties文件里面的内容进行解析,并完成数据库的连接。
	public static void loadDatabaseConfig(String congifFile) throws ClassNotFoundException, SQLException {
		PropertiesParser property = new PropertiesParser();
		property.loadProperties(congifFile);
		
		Class.forName(property.getValue("driver"));
		connection = DriverManager.getConnection(property.getValue("url"), property.getValue("user"),property.getValue("password"));
	}
	
	//调用TableClassFactory类的方法,将XML文件进行解析,将解析出来的内容形成一个map
	public static void loadOrmMapping(String xmlPath) {
		TableClassFactory.scanTableClassMapping(xmlPath);
	}
	
	//通过对这个方法,可以达到:只需要给一个类的名字,我们就可以得到这个类成员在数据库中对应的内容分别是什么。
	public <T> List<T> list(Class<?> klass) {
		List<T> result = new ArrayList<T>();
		
		StringBuilder str = new StringBuilder("SELECT ");
		new TableClassFactory();
		TableClassDefinition tcd = TableClassFactory.getTableClass(klass);	
		//形成一个查询的sql语句
		str.append(tcd.columnList()).append(" FROM").append(" ").append(tcd.getTable());
		//得到了一个字段名字的列表
		List<Property> propertyList = tcd.columnNameList();
		try {
			//开始访问数据库
			PreparedStatement state = connection.prepareStatement(str.toString());
			ResultSet rs = state.executeQuery();
			
			while (rs.next()) {
				try {
					@SuppressWarnings("unchecked")
					//这里是<反射机制>的运用
					T obj = (T) klass.newInstance();
					//这一步执行完,形成了klass类的一个新的对象——obj;
					
					for (Property pro : propertyList) {	//pro 是klass类的成员的名字。
						//(程序写到这里,,,有些同学可能会有个疑问,,,
						//最开始不是说过,我自己写的NativeInfo类,故意把成员名字和数据库中的字段名字写的不一样了么,
						//这里的value对象的成员名字不应该是它对应的字段名字啊,应该get的是property所对应的名字啊,
						//这里为什么要写成getColumn()呢??????
						Object value = rs.getObject(pro.getColumn());
						//答案很简单,我现在是在做工具,我这套工具的目的是:给我一个类,我可以直接得到这个类所对应的数据库中的所有记录
						//那么,我是要得到结果的。为了得到结果,我肯定需要从数据库表里面找数据,再把数据设置到那个类中,
						//既然是在数据库中找结果,我肯定要和数据库里面的字段名字一样才可以,
						//和类中的名字一样,和字段的名字不一样,那肯定是在数据库中找不到结果的啊!
						//所以,我才需要pro.getColumn();
						//之所以返回的是Object类型,是为了这套工具的通用性,如果单纯的返回一个int,
						//那这套工具的通用性就不是很强了。
						setValue(klass, obj, pro, value);//这个方法其实就是通过<反射机制>来执行klass类中的setXXX()方法;
					}
					
					//最后,在把这个已经设置了值的对象放到list里面;
					result.add(obj);
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return result;
	}
	
	/**
	 * @param klass	用来进行反射
	 * @param object		是要完成成员赋值的那个对象
	 * @param propertyName	成员名称
	 * @param value		成员的值
	 */
	public void setValue(Class<?> klass,Object object,Property property,Object value) {
		String name = property.getPropertyName();
		String methodName = "set" + name.substring(0, 1).toUpperCase()
				+ name.substring(1);
		//这两步操作,形成了一个setXXX()方法,(其实就是klass类中的set方法)
		
		try {
			Method method = klass.getDeclaredMethod(methodName, new Class<?>[] {property.getType()});
			//执行着个方法,把从数据库中取得到的值设置进去。
			method.invoke(object, new Object[] {value});
		} catch (Exception e) {
		}
	}
}

最后编写我们的Test :

public class Test {

	public static void main(String[] args) {
			try {
				MecDatabase database = new MecDatabase();
				MecDatabase.loadDatabaseConfig("/mecOrm.properties");
				MecDatabase.loadOrmMapping("/morm.tcm.xml");
				
				List<NativeInfo>list = database.list(NativeInfo.class);          
				
				for (NativeInfo one : list) {
					System.out.println(one);
				}                             
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
	}

}

结果也展示出来:
在这里插入图片描述
通过上述可以看到:我们的小框架基本上很顺利的完成了。所得到的结果,也正如预想那样。只需要给出类名字,就可以得到对应表里面的所有的数据,并且,把那些数据形成了一个 list 进行输出。
事实上,有了这个小框架之后,我们需要编写的仅仅是配置文件,还有一个类。以后,假如我们想再得到数据库里面一个表的所有内容,我只需根据数据库表里面的字段,先形成一个类,再把XML文件进行一个简单的配置,就可以得到结果。这是非常凶悍的。
上面实现的这个,可以说是实现了一个简易的 hibernate 框架,其中的优点也正如最开始所提到的那样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值