【Java】持久层模拟(Hibernate)

Hibernate框架是一个全自动的ORM框架,将数据库的操作转换为对对象的操作以简化开发。它对JDBC进行了轻量级的封装,将POJO与数据库中的表建立映射关系。该框架可以在任何使用JDBC的场合应用(只需修改配置文件即可完成对不同种类数据库的操作),hibernate推荐的HQL查询语言提供了更加丰富的和灵活的查询特性,并且完成数据持久化。

           因为JVM提供垃圾回收机制,所以Hibernate为了实现数据持久化将数据保存在Session会话中,将数据库的访问变成对Session会话的访问,之后通过自动或手动方式再同步到数据库中。以减少对数据库的访问量,降低程序开发代价。

 

首先是成员和字段的基本设置:

package com.chy.database.core;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;

// 成员和字段的对应关系
public class PropertyColumn {
	// 成员
	Field field;
	// 字段
	String column;
	
	PropertyColumn() {
	}

	Field getField() {
		return field;
	}

	void setField(Field field) {
		this.field = field;
	}
	
	void setField(ResultSet rs, Object object) {
		try {
			Object value = rs.getObject(column);
			System.out.println("value:" + value);
			field.setAccessible(true);
			field.set(object, value);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	String getColumn() {
		return column;
	}

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

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((field == null) ? 0 : field.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PropertyColumn other = (PropertyColumn) obj;
		if (field == null) {
			if (other.field != null)
				return false;
		} else if (!field.equals(other.field))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return field.getName() + "=>" + column;
	}
	
}

ClassTableDefinition类:

类和表的映射定义,数据库中的每一条记录对应一个PropertyColumn对象存放在Map中。

package com.chy.database.core;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;

public class ClassTableDefinition {
	// 表名称
	String table;
	// 表所对应的类
	Class<?> klass;
	// 存放成员和字段的映射
	Map<String, PropertyColumn> properties;
	// 关键字
	PropertyColumn id;
	// 字段list(一次查询多项)
	String columnList;
	
	String questionList;
	
	ClassTableDefinition() {
		this.properties = new HashMap<>();
	}

	String getColumnList() {
		if (columnList == null) {
			boolean first = true;
			StringBuffer res = new StringBuffer();
			
			for (String property : properties.keySet()) {
				PropertyColumn propertyColumn = properties.get(property);
				
				res.append(first ? "" : ",").append(table).append(".")
				.append(propertyColumn.getColumn());
				first = false;
			}

			columnList = res.toString();
		}
		
		return columnList;
	}
	
	String getQuestionList() {
		if (questionList == null) {
			int columnCount = properties.size();
			StringBuffer res = new StringBuffer();
			
			for (int i = 0; i < columnCount; i++) {
				res.append(i == 0 ? "" : ",")
				.append('?');
			}
			questionList = res.toString();
		}
		
		return questionList;
	}
	
	void setField(ResultSet rs, Object object) {
		for (PropertyColumn pc : properties.values()) {
			pc.setField(rs, object);
		}
	}
	
	String getTable() {
		return table;
	}

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

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

	void setKlass(Class<?> klass) {
		this.klass = klass;
	}

	PropertyColumn getId() {
		return id;
	}

	void setId(PropertyColumn id) {
		this.id = id;
	}

	// 添加字段
	void addColumn(PropertyColumn propertyColumn) {
		if (properties.containsValue(propertyColumn)) {
			return;
		}
		properties.put(propertyColumn.getField().getName(), propertyColumn);
	}
	
	// XML配置的get方法
	PropertyColumn getPropertyColumn(String property) {
		return properties.get(property);
	}
	
	// 注解配置的get方法
	PropertyColumn getPropertyColumn(Field field) {
		return properties.get(field.getName());
	}
	
	Map<String, PropertyColumn> getProperties() {
		return properties;
	}

	@Override
	public String toString() {
		StringBuffer res = new StringBuffer("class:");
		res.append(klass.getName()).append('\n')
		.append("table:").append(table).append('\n')
		.append("fields:\n");
		
		for (String key : properties.keySet()) {
			PropertyColumn pc = properties.get(key);
			res.append('\t').append(pc).append('\n');
		}
		
		res.append("id:").append(id);
		
		return res.toString();
	}
	
}

工厂类:

package com.chy.database.core;

import java.util.HashMap;
import java.util.Map;

public class ClassTableFactory {
	static final Map<String, ClassTableDefinition> classTableMap = new HashMap<>();
	
	ClassTableFactory() {
	}
	
	void addClassTableDefinition(String className, ClassTableDefinition ctd) {
		if (classTableMap.containsKey(className)) {
			return;
		}
		classTableMap.put(className, ctd);
	}
	
	static ClassTableDefinition getClassTableDefinition(String className) {
		return classTableMap.get(className);
	}
	
	static ClassTableDefinition getClassTableDefinition(Class<?> klass) {
		return getClassTableDefinition(klass.getName());
	}
	
	public Map<String, ClassTableDefinition> getClassTableMap() {
		return classTableMap;
	}
	
}

在这里要完成注解和XML两种配置方式,那么将两者的相同操作写入一个类, 再分别继承。

package com.chy.database.core;

import java.lang.reflect.Field;

public class ClassPathApplicationContext {
	
	ClassPathApplicationContext() {
	}
	
	// 扫描成员
	void scanFields(Class<?> klass, ClassTableDefinition ctd) {
		Field[] fields = klass.getDeclaredFields();
		
		for (Field field : fields) {
			PropertyColumn propertyColumn = new PropertyColumn();
			set(propertyColumn, field, field.getName());
			
			ctd.addColumn(propertyColumn);
		}
	}
	
	// 给classTableDefinition设置类名和表名
	void set(ClassTableDefinition ctd, Class<?> klass, String table) {
		ctd.setKlass(klass);
		ctd.setTable(table);
	}
	
	// 给propertyColumn对象赋值
	void set(PropertyColumn propertyColumn, Field field, String column) {
		propertyColumn.setField(field);
		propertyColumn.setColumn(column);
	}
	
	// 设置关键字
	void setID(ClassTableDefinition classTableDefinition, PropertyColumn id)
			throws Exception {
		if (classTableDefinition.getId() != null) {
			throw new Exception("ID(" + id.getField().getName() + ")重复!");
		}
		classTableDefinition.setId(id);
	}
	
}
package com.chy.database.core;

import java.lang.reflect.Field;

import com.chy.database.annotation.Column;
import com.chy.database.annotation.ID;
import com.chy.database.annotation.Table;
import com.mec.util.PackageScanner;

public class ClassPathAnnotationApplicationContext extends ClassPathApplicationContext{

	public ClassPathAnnotationApplicationContext(String packageName) {
		ClassTableFactory ctf = new ClassTableFactory();
		
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isPrimitive() 
						|| klass.isArray()
						|| klass.isInterface()
						|| klass.isAnnotation()
						|| klass.isEnum()
						|| !klass.isAnnotationPresent(Table.class)) {
					return;
				}
				
				// 扫描带有@Table注解的类,并得到表名称
				Table table = klass.getAnnotation(Table.class);
				String tableName = table.value();
				
				ClassTableDefinition ctd = new ClassTableDefinition();
				set(ctd, klass, tableName);
				
				scanFields(klass, ctd);
				try {
					scanFields(klass.getDeclaredFields(), ctd);
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				ctf.addClassTableDefinition(klass.getName(), ctd);
			}
		}.scannerPackage(packageName);
	}
	
	// 注解扫描
	private void scanFields(Field[] fields, ClassTableDefinition ctd) 
			throws Exception {
		for (Field field : fields) {
			PropertyColumn propertyColumn = ctd.getPropertyColumn(field);
			// 查找是否存在@ID注解
			if (field.isAnnotationPresent(ID.class)) {
				setID(ctd, propertyColumn);
			}
			
			// 若不存在@Column注解,说明字段与成员名称相同,不需要另外设置
			if (!field.isAnnotationPresent(Column.class)) {
				continue;
			}
			Column column = field.getAnnotation(Column.class);
			String columnName = column.value();
			set(propertyColumn, field, columnName);
		}
	}
	
//	public Map<String, ClassTableDefinition> getClassTableFactory() {
//		return new ClassTableFactory().getClassTableMap();
//	}
//	
}
package com.chy.database.core;

import java.lang.reflect.Field;

import org.w3c.dom.Element;

public class ClassPathXMLApplicationContext extends ClassPathApplicationContext{
	
	public ClassPathXMLApplicationContext(String xmlPath) {
		ClassTableFactory ctf = new ClassTableFactory();
		
		new XMLParser() {
			
			@Override
			public void dealElement(Element element, int index) {
				String className = element.getAttribute("class");
				String tableName = element.getAttribute("table");

				try {
					Class<?> klass = Class.forName(className);
					ClassTableDefinition ctd = new ClassTableDefinition();
					set(ctd, klass, tableName);
					scanFields(klass, ctd);
					
					new XMLParser() {
						
						@Override
						public void dealElement(Element element, int index) {
							String column = element.getAttribute("name");
							String property = element.getAttribute("property");
							String id = element.getAttribute("id");
							
							PropertyColumn propertyColumn = ctd.getPropertyColumn(property);
							if (id != "") {
								try {
									// XML解析时,关键字的类型也是PropertyColumn类,
									// 因为无法设置成员field,那么我们将name的值作为field,构成对象pc,并setID
									PropertyColumn pc = new PropertyColumn();
									pc.setColumn(id);
									Field field = propertyColumn.field;
									pc.setField(field);
									setID(ctd, pc);
								} catch (Exception e) {
									e.printStackTrace();
								}
							}
							propertyColumn.setColumn(column);
						}
					}.dealElementInTag(element, "column");
					
					ctf.addClassTableDefinition(className, ctd);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}.dealElementInTag(XMLParser.getDocument(xmlPath), "class_table");
	}
	
//	public Map<String, ClassTableDefinition> getClassTableFactory() {
//		return new ClassTableFactory().getClassTableMap();
//	}
	
}

 重写Connection

package com.chy.database.core;

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

// 重写java.sql.Connection,判断是否连接,并定时检测,随时关闭连,接释放资源
public class Connection {
	private java.sql.Connection connection;
	private boolean isUsed;
	private long time;
	
	public Connection() {
		isUsed = false;
	}
	
	Connection getConnection() {
		time = System.currentTimeMillis();
		isUsed = true;
		return this;
	}
	
	void setConnection(java.sql.Connection connection) {
		this.connection = connection;
	}
	
	// 连接异常关闭(超时)
	void forceClose(long timeOut, long curTime) {
		if (!isUsed) {
			return;
		}
		long delayTime = curTime - time;
		if (timeOut <= delayTime) {
			isUsed = false;
		}
	}
	
	PreparedStatement prepareStatement(String SQLString) throws SQLException {
		return connection.prepareStatement(SQLString);
	}
	
	boolean isUsed() {
		return isUsed;
	}
	
	void setUsed(boolean isUsed) {
		this.isUsed = isUsed;
	}
	
	void close() {
		isUsed = false;
	}
	
}

下面是建立数据库连接池 :

数据库连接是一个有限且昂贵的资源,而数据库连接池则是为了提高程序的性能来分配和管理数据库连接。连接池基本的思想是在系统初始化时,将数据库连接作为对象存储在内存中,当用户访问数据库时,从连接池中取出一个已建立的空闲连接对象,使用完毕后,将连接放回连接池中,以供下一个请求访问使用,而且连接的建立、断开都由连接池自身来管理。

它允许程序重复使用一个现有的数据库连接,当空闲时间超过最大空闲时间时,则释放数据库连接。该数据库连接池只初始化一次,并且创建一定数量的数据库连接放到连接池中,数量是由最小数据库连接数限制。当程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

package com.chy.database.core;

import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;

import com.mec.util.PropertiesParser;

public class DatabaseConnectionPool {
	static final List<Connection> connectionPool = new ArrayList<>();
	private static String driver;
	private static String url;
	private static String user;
	private static String password;

	private static int minCount;
	private static int maxCount;
	private static int deltaCount;
	private static long timeout = 2000;
	
	// 在数据库连接池中创立连接;
	// 需要限制连接数量
	public static boolean createConnection(int count) throws Exception {
		int i = 0;
		
		while (connectionPool.size() < maxCount && i < count) {
			Connection connection = new Connection();
			connection.setConnection(DriverManager.getConnection(url, user, password));
			connectionPool.add(connection);
			i++;
		}
		
		return connectionPool.size() < maxCount;
	}
	
	long timeout() {
		return timeout;
	}
	
	Connection getConnection() throws Exception {
		for (int i = 0; i < connectionPool.size(); i++) {
			Connection connection = connectionPool.get(i);
			if (!connection.isUsed()) {
				return connection.getConnection();
			}
		}
		
		if (createConnection(deltaCount)) {
			return getConnection();
		}
		
		return null;
	}
	
	// 数据库的初始化,包括登录连接数据库的信息均以properties文件的形式存储
	// 不同的数据库登录和连接池的基本设置均不需要修改源代码
	static void initDatabase(String path) throws Exception {
		PropertiesParser.loadProperties(path);
		driver = PropertiesParser.value("driver");
		url = PropertiesParser.value("url");
		user = PropertiesParser.value("user");
		password = PropertiesParser.value("password");
		
		String minCountStr = PropertiesParser.value("minCount");
		minCount = minCountStr == null ? 3 : Integer.valueOf(minCountStr);
		
		String maxCountStr = PropertiesParser.value("maxCount");
		maxCount = maxCountStr == null ? 50 : Integer.valueOf(maxCountStr);
		
		String deltaCountStr = PropertiesParser.value("deltaCount");
		deltaCount = deltaCountStr == null ? 3 : Integer.valueOf(deltaCountStr);
		
		String timeoutStr = PropertiesParser.value("timeout");
		timeout = timeoutStr == null ? 50000 : Long.valueOf(timeoutStr);
		
		Class.forName(driver);
		createConnection(minCount);
	}
	
}
package com.chy.database.core;

public class DataSource {
	
	public static void initDatabase(String packageName) {
		try {
			DatabaseConnectionPool.initDatabase("/database.cfg.properties");
			new ClassPathAnnotationApplicationContext(packageName);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 SQL语句的生成(这里只完成了部分功能):

package com.chy.database.core;

public class Expression {
	
	Expression() {
	}
	
	String baseSelectExpression(ClassTableDefinition ctd) {
		// SELECT (表名称.字段)表 FROM 表名称
		StringBuffer res = new StringBuffer("SELECT ");
		res.append(ctd.getColumnList()).append(" FROM ")
		.append(ctd.getTable());
		
		return res.toString();
	}
	
	String selectExpressionById(ClassTableDefinition ctd) {
		StringBuffer res = new StringBuffer();
		
		PropertyColumn id = ctd.getId();
		res.append(baseSelectExpression(ctd))
		.append(" WHERE ");
		res.append(ctd.getTable()).append(".")
		.append(id.getColumn()).append("=?");
		
		return res.toString();
	}
	
	String insertExpression(ClassTableDefinition ctd) {
		// INSERT INTO xxx (字段1,字段2,……,字段n) VALUES (值1,值2,……,值n)
		StringBuffer res = new StringBuffer("INSERT INTO ");
		res.append(ctd.getTable()).append(" (")
		.append(ctd.getColumnList()).append(") VALUES(")
		.append(ctd.getQuestionList()).append(')');
		
		return res.toString();
	}
	
}

 SQL语句的执行:

package com.chy.database.core;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Query {
	// 数据库连接池只需要一份,因此采用单例模式
	private static final DatabaseConnectionPool databaseConnectionPool =
			new DatabaseConnectionPool();
	
	public Query() {
	}
	
	public int save(Object object) {
		Class<?> klass = object.getClass();
		ClassTableDefinition ctd = ClassTableFactory.getClassTableDefinition(klass);
		if (ctd == null) {
			return 0;
		}
		
		String SQLString = new Expression().insertExpression(ctd);
		try {
			Connection connection = databaseConnectionPool.getConnection();
			PreparedStatement state = connection.prepareStatement(SQLString);
			
			Map<String, PropertyColumn> pcMap = ctd.getProperties();
			int index = 1;
			for (String property : pcMap.keySet()) {
				PropertyColumn propertyColumn = pcMap.get(property);
				state.setObject(index++, propertyColumn.getFieldValue(object));
			}
			return state.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return 0;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> get(Class<?> klass) {
		List<T> res = new ArrayList<>();
		
		try {
			Connection connection = databaseConnectionPool.getConnection();
			ClassTableDefinition ctd = ClassTableFactory.getClassTableDefinition(klass);
			String SQLString = new Expression().baseSelectExpression(ctd);
			PreparedStatement preparedStatement = connection.prepareStatement(SQLString);
			ResultSet rs = preparedStatement.executeQuery();
			
			while (rs.next()) {
				Object obj = klass.newInstance();
				ctd.setField(rs, obj);
				res.add((T) obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T get(Class<?> klass, Object id) {
		try {
			Connection connection = databaseConnectionPool.getConnection();
			ClassTableDefinition ctd = ClassTableFactory.getClassTableDefinition(klass);
			String SQLSring= new Expression().selectExpressionById(ctd);
			PreparedStatement preparedStatement = connection.prepareStatement(SQLSring);
			preparedStatement.setObject(1, id);
			ResultSet rs = preparedStatement.executeQuery();
			
			if (rs.next()) {
				// 将数据库里的每一条信息保存为PropertyColumn对象,放在对应的ClassTableDefinition
				Object obj = klass.newInstance();
				ctd.setField(rs, obj);
				return (T) obj;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
}

(笔记) HibernateMybatis的区别:

           Hibernate是一个工业性极高,非常全面的持久化框架,其功能强大,数据库无关性好,可维护性和可移植性均优于MyBatisMyBatis可以进行更为细致的SQL优化,可以减少查询字段。因为相对于MyBatis,Hibernate更加的复杂、沉重,所以在一般的中小型项目中,Hibernate就被MyBatis替代。但是在数据库进行移植时,MyBatis就会很麻烦。而Hibernate只需要在它配置文件中选择对应的数据库方言即可,非常简单方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值