阿里架构阿里架构师手把手带你a学Mybatis开源ORM框架“

简介

前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架。一直没有时间去补充相应的文档,现在正好抽时间去整理下。通过思路历程和代码注释,一方面重温下知识,另一方面准备后期去完善这个框架。

image.png

传统JDBC连接

参照传统的JDBC连接数据库过程如下,框架所做的事情就是把这些步骤进行封装。

// 1\. 注册 JDBC 驱动Class.forName(JDBC_DRIVER);
// 2\. 打开链接conn = DriverManager.getConnection(DB_URL,USER,PASS);
// 3\. 实例化statementstmt = conn.prepareStatement(sql);
// 4\. 填充数据stmt.setString(1,id);
// 5\. 执行Sql连接ResultSet rs = stmt.executeQuery();
// 6\. 结果查询while(rs.next()){    
// 7\. 通过字段检索    
int id  = rs.getInt("id");    
String name = rs.getString("name");    
String url = rs.getString("url");}

思路历程

上述是执行Java连接数据库通用语句,ORM框架就是对象关系映射框架。我们需要做的重点是步骤4和7,将对象字段填充至数据库,将数据库的内容取出作为对象返回。了解了基本的方法后,开始动手写一个框架了。

数据库驱动

开始,需要构建一个驱动注册类,这个类主要用来对数据库驱动进行构造。详见代码块1

代码块1

数据库驱动类(com.simple.ibatis.dirver)

/** * @Author  xiabing * @Desc    驱动注册中心,对数据库驱动进行注册的地方 **/public class DriverRegister {    /*     * mysql的driver类     * */    private static final String MYSQLDRIVER = "com.mysql.jdbc.Driver";    /*     * 构建driver缓存,存储已经注册了的driver的类型     * */    private static final Map<String,Driver> registerDrivers = new ConcurrentHashMap<>();    /*    *   初始化,此处是将DriverManager中已经注册了驱动放入自己缓存中。当然,你也可以在这个方法内注册        常见的数据库驱动,这样后续就可以直接使用,不用自己手动注册了。    * */    static {        Enumeration<Driver> driverEnumeration = DriverManager.getDrivers();        while (driverEnumeration.hasMoreElements()){            Driver driver = driverEnumeration.nextElement();            registerDrivers.put(driver.getClass().getName(),driver);        }    }    /*     *  加载mysql驱动,此个方法可以写在静态代码块内,代表项目启动时即注册mysql驱动     * */    public void loadMySql(){       if(! registerDrivers.containsKey(MYSQLDRIVER)){           loadDriver(MYSQLDRIVER);       }    }    /*     *  加载数据库驱动通用方法,并注册到registerDrivers缓存中,此处是注册数据库驱动的方法     * */    public void loadDriver(String driverName){        Class<?> driverType;        try {            // 注册驱动,返回驱动类            driverType = Class.forName(driverName);            // 将驱动实例放入驱动缓存中            registerDrivers.put(driverType.getName(),(Driver)driverType.newInstance());        }catch (ClassNotFoundException e){            throw new RuntimeException(e);        }catch (Exception e){            throw new RuntimeException(e);        }    }}

驱动注册后,需要建立数据库连接。但是框架如果一个请求建一个连接,那木势必耗费大量的资源。所以该框架引入池化的概念,对连接进行池化管理。详情见代码2

代码块2

数据源类(com.simple.ibatis.datasource)

PoolConnection

/** * @Author  xiabing * @Desc    连接代理类,是对Connection的一个封装。除了提供基本的连接外,还想记录            这个连接的连接时间,因为有的连接如果一直连接不释放,那木我可以通过查看            这个连接已连接的时间,如果超时了,我可以主动释放。 **/public class PoolConnection {        // 真实的数据库连接    public Connection connection;        // 数据开始连接时间    private Long CheckOutTime;        // 连接的hashCode    private int hashCode = 0;    public PoolConnection(Connection connection) {        this.connection = connection;        this.hashCode = connection.hashCode();    }    public Long getCheckOutTime() {        return CheckOutTime;    }    public void setCheckOutTime(Long checkOutTime) {        CheckOutTime = checkOutTime;    }        // 判断两个PoolConnection对象是否相等,其实是判断其中真实连接的hashCode是否相等    @Override    public boolean equals(Object obj) {        if(obj instanceof  PoolConnection){            return connection.hashCode() ==                    ((PoolConnection) obj).connection.hashCode();        }else if(obj instanceof Connection){            return obj.hashCode() == hashCode;        }else {            return false;        }    }    @Override    public int hashCode() {        return hashCode;    }}

NormalDataSource

/** * @Author  xiabing * @Desc    普通数据源,这个数据源是用来产生数据库连接的。来一个请求就会建立一个数据库连接,没有池化的概念 **/public class NormalDataSource implements DataSource{        // 驱动名称    private String driverClassName;        // 数据库连接URL    private String url;        // 数据库用户名    private String userName;        // 数据库密码    private String passWord;    // 驱动注册中心    private DriverRegister driverRegister = new DriverRegister();    public NormalDataSource(String driverClassName, String url, String userName, String passWord) {        // 初始化时将驱动进行注册        this.driverRegister.loadDriver(driverClassName);        this.driverClassName = driverClassName;        this.url = url;        this.userName = userName;        this.passWord = passWord;    }        // 获取数据库连接    @Override    public Connection getConnection() throws SQLException {        return DriverManager.getConnection(url,userName,passWord);    }        // 移除数据库连接,此方法没有真正移除,只是将连接中未提交的事务进行回滚操作。    public void removeConnection(Connection connection) throws SQLException{        if(!connection.getAutoCommit()){            connection.rollback();        }    }        // 获取数据库连接    @Override    public Connection getConnection(String username, String password) throws SQLException {        return DriverManager.getConnection(url,username,password);    }        // 后续方法因为没有用到,所有没有进行重写了    @Override    public <T> T unwrap(Class<T> iface) throws SQLException {        return null;    }    @Override    public boolean isWrapperFor(Class<?> iface) throws SQLException {        return false;    }    @Override    public PrintWriter getLogWriter() throws SQLException {        return null;    }    @Override    public void setLogWriter(PrintWriter out) throws SQLException {    }    @Override    public void setLoginTimeout(int seconds) throws SQLException {    }    @Override    public int getLoginTimeout() throws SQLException {        return 0;    }    @Override    public Logger getParentLogger() throws SQLFeatureNotSupportedException {        return null;    }}

PoolDataSource

/** * @Author  xiabing * @Desc    池化线程池,对连接进行管理 **/public class PoolDataSource implements DataSource{    private Integer maxActiveConnectCount = 10; // 最大活跃线程数,此处可根据实际情况自行设置    private Integer maxIdleConnectCount = 10; // 最大空闲线程数,此处可根据实际情况自行设置    private Long maxConnectTime = 30*1000L; // 连接最长使用时间,在自定义连接中配置了连接开始时间,在这里来判断该连接是否超时    private Integer waitTime = 2000; // 线程wait等待时间    private NormalDataSource normalDataSource; // 使用normalDataSource来产生连接    private Queue<PoolConnection> activeConList = new LinkedList<>(); // 存放活跃连接的队列    private Queue<PoolConnection> idleConList = new LinkedList<>(); // 存放空闲连接的队列    public PoolDataSource(String driverClassName, String url, String userName, String passWord) {        this(driverClassName,url,userName,passWord,10,10);    }    public PoolDataSource(String driverClassName, String url, String userName, String passWord,Integer maxActiveConnectCount,Integer maxIdleConnectCount) {        // 初始化normalDataSource,因为normalDataSource已经封装了新建连接的方法        this.normalDataSource = new NormalDataSource(driverClassName,url,userName,passWord);        this.maxActiveConnectCount = maxActiveConnectCount;        this.maxIdleConnectCount = maxIdleConnectCount;    }    /**     * @Desc 获取连接时先从空闲连接列表中获取。若没有,则判断现在活跃连接是否已超过设置的最大活跃连接数,没超过,new一个     *        若超过,则判断第一个连接是否已超时,若超时,则移除掉在新建。若未超时,则wait()等待。     **/    @Override    public Connection getConnection(){        Connection connection = null;        try {            connection =  doGetPoolConnection().connection;        }catch (SQLException e){            throw new RuntimeException(e);        }        return connection;    }    public void removeConnection(Connection connection){        PoolConnection poolConnection = new PoolConnection(connection);        doRemovePoolConnection(poolConnection);    }        private PoolConnection doGetPoolConnection() throws SQLException{        PoolConnection connection = null;        while (connection == null){            // 加锁            synchronized (this){                // 判断是否有空闲连接                if(idleConList.size() < 1){                     // 判断活跃连接数是否已经超过预设的最大活跃连接数                    if(activeConList.size() < maxActiveConnectCount){                        // 如果还可以新建连接,就新建一个连接                        connection = new PoolConnection(normalDataSource.getConnection());                    }else {                        // 走到这一步,说明没有空闲连接,并且活跃连接已经满了,则只能看哪些连接超时,判断第一个连接是否超时                        PoolConnection poolConnection = activeConList.peek();                        // 这就是我为啥要给数据库连接加连接时间了                        if(System.currentTimeMillis() - poolConnection.getCheckOutTime() > maxConnectTime){                            // 若第一个连接已经超时了,移除第一个活跃连接                            PoolConnection timeOutConnect = activeConList.poll();                            if(!timeOutConnect.connection.getAutoCommit()){                                // 如果该连接设的是非自动提交,则对事物进行回滚操作                                timeOutConnect.connection.rollback();                            }                            // 置为空,让垃圾收集器去收集                            timeOutConnect.con
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值