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