内容简介:公司的持久层采用的hibernate框架,这也是很多公司使用的一种持久层框架。它将瞬时态的数据转化为持久态、或将持久态的数据转化为瞬时态数据。我比较喜欢看源码,看别人的架构思想,因为,笔者想向架构师的方向进发。看了别人的源码,突然想模拟hibernate框架,自己写个框架出来。 这里去除了hibernate框架晦涩的地方,当做自己学习材料还是不错的。里面涉及到反射、连接池等等。 这个项目中,你可以知道数据库连接池是怎么建的,又是怎么回收的。 使用警惕代码块加载配置文件以下详细介绍我个人的项目,但肯定没有人家
导读
公司的持久层采用的hibernate框架,这也是很多公司使用的一种持久层框架。它将瞬时态的数据转化为持久态、或将持久态的数据转化为瞬时态数据。我比较喜欢看源码,看别人的架构思想,因为,笔者想向架构师的方向进发。看了别人的源码,突然想模拟hibernate框架,自己写个框架出来。 这里去除了hibernate框架晦涩的地方,当做自己学习材料还是不错的。里面涉及到反射、连接池等等。 这个项目中,你可以知道数据库连接池是怎么建的,又是怎么回收的。 使用警惕代码块加载配置文件
以下详细介绍我个人的项目,但肯定没有人家源码写得好,这里仅作为学习使用。
如果不懂的,可以私信我。
配置文件
本项目以idea为开发环境和以maven搭建的,分为 java 包和test包。java包的配置文件放在resources下,代码放在com.zby.simulationHibernate包下,如下是配置文件:
连接池
我们在使用hibernate时,一般会配置连接池,比如,初始化连接数是多少,最大连接数是多少?这个连接的是什么?我们在启动项目时,hibernate根据初始的连接数,来创建多少个数据库连接对象,也就是jdbc中的Connection对象。
为什么要有这个连接池?因为,每次开启一个连接和关闭一个连接都是消耗资源的,我们开启了这些连接对象之后,把它们放在一个容器中,我们何时需要何时从容器中取出来。当不需要的时候,再将踏进放回到容器中。因而,可以减少占用的资源。
如下,是初始化的连接对象:
package com.zby.simulationHibernate.util.factory;
import com.zby.simulationHibernate.util.exception.GenericException;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* Created By zby on 21:23 2019/1/23
* 数据库的连接
*/
public class Connect {
/**
* 连接池的初始值
*/
private static int initPoolSize = 20;
/**
* 创建property的配置文件
*/
protected static Properties properties;
/**
* 连接池的最小值
*/
protected static int minPoolSize;
/**
* 连接池的最大值
*/
protected static int maxPoolSize;
//【2】静态代码块
static {
//加载配置文件
properties = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("db.properties");
try {
properties.load(is);
minPoolSize = Integer.valueOf(properties.getProperty("jdbc.minConnPool"));
if (minPoolSize <= initPoolSize)
minPoolSize = initPoolSize;
maxPoolSize = Integer.valueOf(properties.getProperty("jdbc.maxConnPool"));
if (minPoolSize > maxPoolSize)
throw new GenericException("连接池的最小连接数不能大于最大连接数");
} catch (IOException e) {
System.out.println("未找到配置文件");
e.printStackTrace();
}
}
/**
* Created By zby on 16:50 2019/1/23
* 获取数据连接
*/
protected java.sql.Connection createConnect() {
String driverName = properties.getProperty("jdbc.driver");
if (StringUtils.isEmpty(driverName)) {
driverName = "com.mysql.jdbc.Driver";
}
String userName = properties.getProperty("jdbc.username");
String password = properties.getProperty("jdbc.password");
String dbUrl = properties.getProperty("jdbc.url");
try {
Class.forName(driverName);
return DriverManager.getConnection(dbUrl, userName, password);
} catch (ClassNotFoundException e) {
System.out.println("找不到驱动类");
e.printStackTrace();
} catch (SQLException e) {
System.out.println("加载异常");
e.printStackTrace();
}
return null;
}
}
创建Session会话
我们在使用hibernate时,不是直接使用连接对象,而是,以会话的方式创建一个连接。创建会话的方式有两种。一种是openSession,这种是手动提交事务。getCurrentSession是自动提交事务。
如代码所示:
package com.zby.simulationHibernate.util.factory;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created By zby on 15:43 2019/1/23
*/
public class SqlSessionFactory implements SessionFactory {
/**
* 连接池
*/
private static List<Connection> connections;
/**
* 连接对象
*
* @return
*/
private static Connect connect = new Connect();
protected static List<Connection> getConnections() {
return connections;
}
//静态代码块,初始化常量池
static {
connections = new ArrayList<>();
Connection connection;
for (int i = 0; i < Connect.minPoolSize; i++) {
connection = connect.createConnect();
connections.add(connection);
}
}
@Override
public Session openSession() {
return getSession(false);
}
@Override
public Session getCurrentSession() {
return getSession(true);
}
/**
* 获取session
*
* @param autoCommit 是否自动提交事务
* @return
*/
private Session getSession(boolean autoCommit) {
//【1】判断连接池有可用的连接对象
boolean hasNoValidConn = hasValidConnction();
//【2】没有可用的连接池,使用最大的连接池
if (!hasNoValidConn) {
for (int i = 0; i < (Connect.maxPoolSize - Connect.minPoolSize); i++) {
connections.add(connect.createConnect());
}
}
//【3】有可用的连接
for (Iterator iterator = connections.iterator(); iterator.hasNext(); ) {
Connection connection = null;
try {
connection = (Connection) iterator.next();
connection.setAutoCommit(autoCommit);
Session session = new Session(connection);
iterator.remove();
return session;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* Created By zby on 21:50 2019/1/23
* 当我们没开启一个连接,连接池的数目减少1,直到连接池的数量为0
*/
private boolean hasValidConnction() {
return null != connections && connections.size() != 0;
}
}
数据查找
我们既然使用这个框架,必然要有数据查找的功能。返回结果分为两种,一种是以实体类直接返回,调用AddEntity方法。但是,有时时多张表查询的结果,这种情况下,直接以实体类肯定不可以的,因而,我们需要使用自定义接收对象,并将查找结果进行过滤,再封装成我们想要的对象。
- 第一种,以实体类返回
/**
* Created By zby on 23:19 2019/1/23
* 体检反射的实体类
*/
public SqlQuery addEntity(Class<T> persistenceClass) {
this.persistenceClass = persistenceClass;
return this;
}
- 第二种,过滤后返回数据
/**
* Created By zby on 19:18 2019/1/27
* 创建类型
*/
public SqlQuery addScalar(String tuple, String alias) {
if (CommonUtil.isNull(aliasMap)) {
aliasMap = new HashMap<>();
}
for (Map.Entry<String, String> entry : aliasMap.entrySet()) {
String key = entry.getKey();
if (key.equals(tuple))
throw new GenericException("alias已经存在,即alias=" + key);
String value = aliasMap.get(key);
if (value.equals(alias) && key.equals(tuple))
throw new GenericException("当前alias的type已经存在,alias=" + key + ",type=" + value);
}
aliasMap.put(tuple, alias);
return this;
}
/**
* Created By zby on 9:20 2019/1/28
* 数据转换问题
*/
public SqlQuery setTransformer(ResultTransformer transformer) {
if (CommonUtil.isNull(aliasMap)) {
throw new IllegalArgumentException("请添加转换的属性数量");
}
transformer.transformTuple(aliasMap);
this.transformer = transformer;
return this;
}
- 以集合的方式返回数据:
/**
* Created By zby on 17:02 2019/1/29
* 设置查找参数
*/
public SqlQuery setParamter(int start, Object param) {
if (CommonUtil.isNull(columnParamer))
columnParamer = new HashMap<>();
columnParamer.put(start, param);
return this;
}
/**
* Created By zby on 16:41 2019/1/24
* 查找值
*/
public List<T> list() {
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
if (CommonUtil.isNotNull(columnParamer)) {
for (Map.Entry<Integer, Object> entry : columnParamer.entrySet()) {
int key = entry.getKey();
Object value = entry.getValue();
statement.setObject(key + 1, value);
}
}
resultSet = statement.executeQuery();
PersistentObject persistentObject = new PersistentObject(persistenceClass, resultSet);
if (CommonUtil.isNotNull(aliasMap))
return persistentObject.getPersist(transformer);
return persistentObject.getPersist();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionClose.closeConnStateResSet(connection, statement, resultSet);
}
return null;
}
- 返回唯一值
/**
* Created By zby on 16:41 2019/1/24
* 查找值
*/
public T uniqueResult() {
List<T> list = list();
if (CommonUtil.isNull(list))
return null;
if (list.size() > 1)
throw new GenericException("本来需要返回一个对象,却返回 " + list.size() + "个对象");
return list.get(0);
}
- 测试
@Test
public void testList() {
Session session = new SqlSessionFactory().openSession();
String sql = "SELECT " +
" customer_name AS customerName, " +
" `name` AS projectName " +
"FROM " +
" project where id >= ? and id <= ?";
SqlQuery query = session.createSqlQuery(sql);
query.setParamter(0, 1);
query.setParamter(1, 2);
query.addScalar("customerName", StandardBasicTypes.STRING)
.addScalar("projectName", StandardBasicTypes.STRING);
query.setTransformer(Transforms.aliasToBean(ProjectData.class));
List<ProjectData> projects = query.list();
for (ProjectData project : projects) {
System.out.println(project.getCustomerName() + " " + project.getProjectName());
}
}
@Ignore
public void testListNoData() {
Session session = new SqlSessionFactory().openSession();
String sql = "SELECT " +
" customer_name AS customerName, " +
" `name` AS projectName " +
"FROM " +
" project where id >= ? and id <= ?";
SqlQuery query = session.createSqlQuery(sql).
setParamter(0, 1).
setParamter(1, 2).
addEntity(Project.class);
List<Project> projects = query.list();
for (Project project : projects) {
System.out.println(project.getCustomerName() + " " + project.getGuestCost());
}
}
保存数据
我们这里以 merger来保存数据,因为这个方法非常的特殊。如果该瞬时态的独享有主键,而且,其在数据库中依旧存在该主键的数据,我们此时就更新数据表。如果数据表中没有当前主键的数据,我们向数据库中添加该对象的值。如果该瞬时态的对象没有主键,我们直接在数据表中添加该对象。
如代码所示:
/**
* Created By zby on 15:41 2019/1/29
* 合并,首先判断id是否存在,若id存在则更新,若id不存在,则保存数据
*/
public T merge(T t) {
if (CommonUtil.isNull(t))
throw new IllegalArgumentException("参数为空");
Class<T> clazz = (Class<T>) t.getClass();
Field[] fields = clazz.getDeclaredFields();
boolean isContainsId = CommonUtil.isNotNull(PropertyUtil.containId(fields)) ? true : false;
long id = PropertyUtil.getIdValue(fields, t, propertyAccessor);
if (isContainsId) {
return id > 0L ? update(t) : save(t);
}
return save(t);
}
/**
* Created By zby on 17:37 2019/1/29
* 保存数据
*/
public T save(T t) {
if (CommonUtil.isNull(t))
throw new RuntimeException("不能保存空对象");
PreparedStatement statement = null;
ResultSet resultSet = null;
StringBuilder columnJoint = new StringBuilder();
StringBuilder columnValue = new StringBuilder();
try {
Field[] fields = t.getClass().getDeclaredFields();
String sql = " insert into " + ClassUtil.getClassNameByGenericity(t) + "(";
for (int i = 0; i < fields.length; i++) {
String propertyName = fields[i].getName();
Object propertyValue = propertyAccessor.getPropertyValue(t, propertyName);
if (CommonUtil.isNotNull(propertyValue)) {
String columnName = PropertyUtil.propertyNameTransformColumnName(propertyName, true);
if (StandardBasicTypes.BOOLEAN.equalsIgnoreCase(fields[i].getGenericType().toString())) {
columnJoint.append("is_" + columnName + ",");
columnValue.append(propertyValue + ",");
} else if (StandardBasicTypes.LONG.equalsIgnoreCase(fields[i].getGenericType().toString())
|| StandardBasicTypes.FLOAT.equalsIgnoreCase(fields[i].getGenericType().toString())
|| StandardBasicTypes.DOUBLE.equalsIgnoreCase(fields[i].getGenericType().toString())
|| StandardBasicTypes.INTEGER.equalsIgnoreCase(fields[i].getGenericType().toString())) {
columnJoint.append(columnName + ",");
columnValue.append(propertyValue + ",");
} else if (StandardBasicTypes.DATE.equalsIgnoreCase(fields[i].getGenericType().toString())) {
columnJoint.append(columnName + ",");
columnValue.append("'" + DateUtil.SIMPLE_DATE_FORMAT.format((Date) propertyValue) + "',");
} else {
columnJoint.append(columnName + ",");
columnValue.append("'" + propertyValue + "',");
}
}
}
columnJoint = StringUtil.replace(columnJoint, ",");
columnValue = StringUtil.replace(columnValue, ",");
sql += columnJoint + ") VALUES(" + columnValue + ")";
System.out.println(sql);
statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
statement.executeUpdate();
resultSet = statement.getGeneratedKeys();
while (resultSet.next()) {
return load((Class<T>) t.getClass(), resultSet.getLong(1));
}
return t;
} catch (SQLException e) {
System.out.println("保存数据出错,实体对象为=" + t);
e.printStackTrace();
} finally {
SessionClose.closeConnStateResSet(connection, statement, resultSet);
}
return null;
}
- 测试代码:
@Test
public void testSave() {
Session session = new SqlSessionFactory().getCurrentSession();
Project project = new Project();
project.setCustomerName("hhhh");
project.setCreateDatetime(new Date());
project.setDeleted(true);
project = (Project) session.save(project);
System.out.println(project.getId());
}
通过id加载对象
有时,我们只要根据当前对象的id,获取当前对象的全部信息,因而,我们可以这样写:
/**
* Created By zby on 16:36 2019/1/29
* 通过id获取对象
*/
public T load(Class<T> clazz, Long id) {
if (CommonUtil.isNull(clazz))
throw new IllegalArgumentException("参数为空");
String className = ClassUtil.getClassNameByClass(clazz);
String sql = " select * from " + className + " where id= ? ";
SqlQuery query = createSqlQuery(sql)
.setParamter(0, id)
.addEntity(clazz);
return (T) query.uniqueResult();
}
测试代码:
@Test
public void testload() {
Session session = new SqlSessionFactory().openSession();
Project project = (Project) session.load(Project.class, 4L);
System.out.println(project);
}
回收连接对象
当我们使用完该连接对象后,需要将对象放回到容器中,而不是直接调用connection.close()方法,而是调用这个方法:
/**
* Created By zby on 16:10 2019/3/17
* 获取容器的对象,如果是关闭session,则将连接对象放回到容器中
* 如果是开启session,则从容器中删除该连接对象
*/
protected static List<Connection> getConnections() {
return connections;
}
/**
* Created By zby on 22:45 2019/1/23
* <p>
* 当关闭当前会话时,这并非真正的关闭会话
* 只是将连接对象放回到连接池中
*/
public static void closeConn(Connection connection) {
SqlSessionFactory.getConnections().add(connection);
}
总结
写框架其实是不难的,难就难在如何设计框架。或者说,难就难在基础不牢。如果基础打不牢的话,很难网上攀升。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python Algorithms
Magnus Lie Hetland / Apress / 2010-11-24 / USD 49.99
Python Algorithms explains the Python approach to algorithm analysis and design. Written by Magnus Lie Hetland, author of Beginning Python, this book is sharply focused on classical algorithms, but it......一起来看看 《Python Algorithms》 这本书的介绍吧!