记一次写框架的心路历程

背景

周五的时候,我的主管把我叫过去说有一个需求很急,采用传统的开发模式可能来不及,他说他打算开发一个框架来快速集成,当然把我叫过去的目的是和他一起开发,他给我的任务是,让我开发一个轻量级的mybatis,大致要求如下两点:

  1. 不要xml文件
  2. 要有缓存

老实讲我之前是没有开发过框架的,并且mybatis我虽然用的是很溜,但是源码我确实没有看过,而且只有一天时间,难度对我来说是挺大的。

于是我继续和我的老大沟通,他具体是要干什么,他说就是能执行sql就好,突然一道灵光乍现,既然这样我们用JDBC不就好了,老大说可以你来封装一个。

思路

JDBC的封装要做哪些事?思考如下

  1. 结果集的封装
  2. 数据库连接池的封装
开干
结果集的封装

结果集我选用map来封装,map中的key截取sql中的column,同时会将sql中的"_"格式改成驼峰。

结果如下:
在这里插入图片描述

数据库连接池的封装

为什么要封装连接池,因为为了稍微可以抗下并发,总不能所有的sql都用一个连接来执行吧,那样速度会慢很多。

看下一个连接的执行效果(100个线程100个并发)
在这里插入图片描述
看下20个连接的效果(100个线程100个并发)
在这里插入图片描述

看到没这就是连接池的好处,速度提升了20倍有没有!!!

数据库连接池的封装的大意是搞一个集合,将连接放进集合,当有sql要执行时,从集合里去不同的连接来执行sql,注意是不同的连接,准确来说是空闲的连接,如果你总是取集合里的一个连接来执行sql,那么和一个连接有什么区别。

目前我的逻辑是轮流取连接,但是当连接取完后,其他的请求就空转等待其他sql执行完毕。

这里有一个逻辑需要注意尽量采用公平锁的策略,即结束空转的请求尽量去取优先释放的连接,否则执行sql的时候还可能会排队,测试过优化后公平策略的效率同样提升了2-3倍。

代码如下:

@Configuration
public class DBUtil {

    @Value("${jdbc.url}")
    private String url;
    @Value("${username}")
    private String user;
    @Value("${password}")
    private String password;
    @Value("${maxConnectNum}")
    private Integer maxConnectNum;
    @Value("${minConnectNum}")
    private Integer minConnectNum;

    private volatile List<Connection> pool = new ArrayList<Connection>();

    private volatile AtomicInteger index = new AtomicInteger(0);

    private Logger logger = LoggerFactory.getLogger(DBUtil.class.getName());

    @PostConstruct
    public void initConnect(){
        for (Integer i = 0; i < minConnectNum; i++) {
            pool.add(connect());
        }
        logger.info("初始化完成,poolSize = {}",pool.size());
    }

    public SqlResult get(String sql) {
        return this.query(sql).get(0);
    }

    public List<SqlResult> query(String sql) {
        Statement statement = null;
        Connection connection = null;
        List<SqlResult> list = new ArrayList<SqlResult>();
        try {
            connection = getConnection();
            statement = connection.createStatement();
            ResultSet res = statement.executeQuery(sql);
            String[] cols = dealSql(sql);
            while (res.next()) {
                SqlResult result = new SqlResult();
                for (String col : cols) {
                    result.put(convert(col), res.getObject(col.replace(" ", "")));
                }
                list.add(result);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (statement != null) {
                try {
                    statement.close();
                    index.decrementAndGet();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }

    private String[] dealSql(String sql) {
        return sql.split("from")[0].split("select")[1].split(",");
    }

    private Connection getConnection()  {
        try{
            if (index.get() == minConnectNum){
                if (pool.size() == minConnectNum){
                    synchronized(DBUtil.class){
                        if (pool.size() == minConnectNum){
                            logger.info("最小连接数已耗尽,index = {}",index.get());
                            for (Integer i = 0; i < maxConnectNum - minConnectNum; i++) {
                                pool.add(connect());
                            }
                            logger.info("最大化完成,poolSize = {}",pool.size());
                        }
                    }
                }
            }
            Connection connection = null;
            if (index.get() >= maxConnectNum){
                synchronized (DBUtil.class){
                    if (index.get() >= maxConnectNum){
                        logger.info("最大连接数已耗尽,index = {}",index.get());
                        while (index.get() >= maxConnectNum){
                        }
                        logger.info("等待连接空转结束,index = {}",index.get());
                    }
                    connection = pool.get(maxConnectNum - index.get());
                    index.incrementAndGet();
                }
            }else {
                connection = pool.get(index.get() % pool.size());
                index.incrementAndGet();
            }
            return connection;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    private Connection connect(){
        try {
            Class.forName("com.mysql.jdbc.Driver");
            return DriverManager.getConnection(url, user, password);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    private String convert(String var1) {
        var1 = var1.replace(" ", "");
        String[] s = var1.split("_");
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < s.length; i++) {
            if (i == 0) {
                res.append(s[i]);
            } else {
                res.append(s[i].substring(0, 1).toUpperCase()).append(s[i].substring(1, s[i].length()));
            }
        }
        return res.toString();
    }
}
总结

写框架与业务代码不同,第一是要封装的足够通用,第二是要兼顾性能。

关注个人公众号:有意思的程序员
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值