资讯专栏INFORMATION COLUMN

模仿hibernate框架,详解hibernate部分方法设计

王陆宽 / 2714人阅读

摘要:导读源码地址公司的持久层采用的框架,这也是很多公司使用的一种持久层框架。配置文件本项目以为开发环境和以搭建的,分为包和包。一种是,这种是手动提交事务。返回结果分为两种,一种是以实体类直接返回,调用方法。

导读

源码地址

公司的持久层采用的hibernate框架,这也是很多公司使用的一种持久层框架。它将瞬时态的javabean对象转化为持久态数据表的字段对象、或将持久态的字段对象转化为瞬时态javabean对象。我比较喜欢看源码,看别人的架构思想,因为,笔者想向架构师的方向进发。看了别人的源码,突然想模拟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 connections;

    /**
     * 连接对象
     *
     * @return
     */
    private static Connect connect = new Connect();

    protected static List 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 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 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 list() {
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    try {
        statement = connection.prepareStatement(sql);
        if (CommonUtil.isNotNull(columnParamer)) {
            for (Map.Entry 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 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 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 projects = query.list();
        for (Project project : projects) {
            System.out.println(project.getCustomerName() + " " + project.getGuestCost());
        }
    }

保存数据

我们这里以 merge 方法来保存数据,因为这个方法非常的特殊,我们在这里做特殊说明。如果该瞬时态的对象有主键,而且,其在数据表中已经存在该主键的字段对象,我们此时就更新该数据表。如果数据表中没有当前主键的字段对象,我们向数据库中添加该对象的值。如果该瞬时态的对象没有主键,我们直接在数据表中添加该对象。

如代码所示:

 /**
 * 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 clazz = (Class) 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.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 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 getConnections() {
    return connections;
}

 /**
 * Created By zby on 22:45 2019/1/23
 * 

* 当关闭当前会话时,这并非真正的关闭会话 * 只是将连接对象放回到连接池中 */ public static void closeConn(Connection connection) { SqlSessionFactory.getConnections().add(connection); }

总结

写框架其实是不难的,难就难在如何设计框架。或者说,难就难在基础不牢。如果基础打不牢的话,很难往上攀升。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73729.html

相关文章

  • Java 程序员必备的 15 个框架,前 3 个地位无可动摇!

    摘要:官网源码推荐从开始手写一个框架更多请在技术栈微信公众号后台回复关键字。是一个开放源代码的对象关系映射框架,它对进行了非常轻量级的对象封装,它将与数据库表建立映射关系,是一个全自动的框架。 Java 程序员方向太多,且不说移动开发、大数据、区块链、人工智能这些,大部分 Java 程序员都是 Java Web/后端开发。那作为一名 Java Web 开发程序员必须需要熟悉哪些框架呢? 今天...

    galaxy_robot 评论0 收藏0
  • 纳税服务系统【总结】

    摘要:要是使用到日历的话,我们想到使用这个日历类上面仅仅是我个人总结的要点,如果有错误的地方还请大家给我指正。 纳税服务系统总结 纳税服务系统是我第一个做得比较大的项目(不同于javaWeb小项目),该项目系统来源于传智Java32期,十天的视频课程(想要视频的同学关注我的公众号就可以直接获取了) 我跟着练习一步一步完成需求,才发觉原来Java是这样用来做网站的,Java有那么多的类库,页面...

    ispring 评论0 收藏0
  • Java 事务详解

    摘要:一个事务的执行不能被其他事务干扰。持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。事务与相关联,并通过调用实例化。强制此事务回滚。为此事务注册用户同步回调。检查事务是否成功提交。 一、事务 (1)事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。 (2)事务应该具有...

    cyrils 评论0 收藏0
  • Hibernate配置及自定义注册映射文件

    摘要:一配置属性详解可以在各式各样不同环境下工作而设计的因此存在着大量的配置参数。以简便操作,多数配置参数都有默认的配置值也是我们日常使用的必须品。 Hibernate (开放源代码的对象关系映射框架) Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装, 它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernat...

    coordinate35 评论0 收藏0
  • Java3y文章目录导航

    摘要:前言由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 前言 由于写的文章已经是有点多了,为了自己和大家的检索方便,于是我就做了这么一个博客导航。 由于更新比较频繁,因此隔一段时间才会更新目录导航哦~想要获取最新原创的技术文章欢迎关注我的公众号:Java3y Java3y文章目录导航 Java基础 泛型就这么简单 注解就这么简单 Druid数据库连接池...

    KevinYan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<