美文网首页
造一个方形的轮子5--数据库支持

造一个方形的轮子5--数据库支持

作者: 爱笑笑_ixx | 来源:发表于2019-07-08 19:20 被阅读0次

    造一个方形轮子文章目录:造一个方形的轮子

    01、先把车正过

    在上一篇《造一个方形的轮子4--依赖注入》的最后提出了一个问题,按类型简称(不带包名)以及按注解上设置的Bean名字去初始化Bean的时候都会有覆盖问题,比如不同包下的相同的类,或者在注解上设置了相同Bean名字的类,解决方法不允许重复就可以了,暴力一点有重名的直接抛异常。

    BeansInitUtil.java 修改loadClass方法:

        //......上略
        private static void loadClass(File file, Map<String, BeanObject> map){
            ....
                    // 按类设置bean
                    map.put(beanObject.getClassName(), beanObject);
                    String simpleName = firstToLowerCase(beanObject.getSimpleName());
                    // 这里添加判断
                    if(map.get(simpleName) != null){
                        throw new SquareBeanInitException("There are duplicate beans ,beanName:"+simpleName);
                    }
                    map.put(simpleName, beanObject);
                    // 按注解输入value设置bean
                    for (Annotation annotation : annotations) {
                        String tmp_name = "";
                        if(annotation instanceof Service){
                            tmp_name = ((Service)annotation).value();
                        } else if(annotation instanceof Component) {
                            tmp_name = ((Component)annotation).value();
                        }
                        if(tmp_name != null && !tmp_name.equals("")) {
                            // 这里添加判断
                            if(map.get(tmp_name) != null){
                                throw new SquareBeanInitException("There are duplicate beans ,beanName:"+tmp_name);
                            }
                            map.put(tmp_name, beanObject);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("init bean error:{}", file.getPath(), e);
            }
        }
        //......下略
    

    复制一个com.jisuye.service.impl.AbcImpl到com.jisuye.service包启动程序查看日志输出:

    12:48:17.058 [main] ERROR com.jisuye.core.SquareApplication - There are duplicate beans ,beanName:abcImpl
    12:48:17.060 [main] ERROR com.jisuye.core.SquareApplication - Application startup failed...
    

    看日志输出了重复的bean异常提示,现在删除刚复制的AbcImpl.java,将原来com.jisuye.service.impl.DefImpl及com.jisuye.service.impl.DefImpl都使用@Service("def") 注解,然后启动程序查看日志输出:

    12:53:53.801 [main] ERROR com.jisuye.core.SquareApplication - There are duplicate beans ,beanName:def
    12:53:53.803 [main] ERROR com.jisuye.core.SquareApplication - Application startup failed...
    

    也输出了输出了重复的bean异常提示,验证完毕。

    02、数据库支持准备

    数据库支持就以mysql为例,添加一个jdbcTemplate类似的功能。

    首先引入数据库链接池,这们就不用管链接的问题了,使用的是HikariCP链接池,在pom.xml中添加依赖

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
                <version>${hikariCP.version}</version>
            </dependency>
    

    配置文件中添加数据库相关配置

    square:
      datasource:
        url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true
        username: root
        password: 123456
    

    03、DbUtil工具类

    将创建数据库链接池及相关方法封装到统一的工具类DbUtil.java:

    package com.jisuye.util;
    //import ...
    /**
     * 数据库操作工具
     * @author ixx
     * @date 2019-07-01
     */
    public class DbUtil {
        private static final Logger log = LoggerFactory.getLogger(DbUtil.class);
        private static Connection connection;
    
        /** 初始化方法*/
        public static void init(){
            try {
                String url = ApplicationContext.getConf("square.datasource.url").toString();
                String username = ApplicationContext.getConf("square.datasource.username").toString();
                String password = ApplicationContext.getConf("square.datasource.password").toString();
    
                HikariDataSource ds = new HikariDataSource();
                ds.setJdbcUrl(url);
                ds.setUsername(username);
                ds.setPassword(password);
                // HikariCP提供的优化设置
                ds.addDataSourceProperty("cachePrepStmts", "true");
                ds.addDataSourceProperty("prepStmtCacheSize", "250");
                ds.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
                connection = ds.getConnection();
            } catch (Exception e) {
                log.error("mysql connection init error..", e);
                throw new SquareBeanInitException("mysql connection init error....");
            }
        }
    
        /** install/update 带参数占位符方法*/
        public static boolean update(String sql, Object... params){
            PreparedStatement statement;
            try {
                statement = connection.prepareStatement(sql);
                if(params != null) {
                    for (int i = 1; i <= params.length; i++) {
                        statement.setObject(i, params[i - 1]);
                    }
                }
                return statement.execute();
            } catch (Exception e) {
                log.error("install/update exception.", e);
            }
            return false;
        }
        /** install/update 无参数占位符方法*/
        public static boolean update(String sql){
            return update(sql, null);
        }
    
        /**
         * 通用查询方法
         * @param sql sql语句
         * @param clazz 返回列表类型
         * @param params 参数列表
         * @param <T> 返回列表类型
         * @return
         */
        public static <T> List<T> select(String sql,Class<T> clazz, Object... params){
            List<T> list = new ArrayList<>();
            PreparedStatement statement;
            try {
                statement = connection.prepareStatement(sql);
                for(int i=1; i<= params.length; i++){
                    statement.setObject(i, params[i-1]);
                }
                ResultSet rs = statement.executeQuery();
                while(rs.next()){
                    T t = clazz.newInstance();
                    Method[] methods = clazz.getMethods();
                    for (Method method : methods) {
                        if(method.getName().startsWith("set")){
                            String field = BeansInitUtil.firstToLowerCase(method.getName().substring(3));
                            method.invoke(t, rs.getObject(field));
                        }
                    }
                    list.add(t);
                }
            } catch (Exception e) {
                log.error("select exception.", e);
            }
            return list.size()>0 ? list : null;
        }
    }
    
    

    init方法用来初始化链接池,update方法执行insert/update/delete语句,select处理查询并简单使用反射封装了一下Entity。

    04、添加JdbcTemplate

    JdbcTemplate类,只是封装了一下DbUtil的方法,提供给其它service使用,代码如下:

    package com.jisuye.core;
    //import ...
    /**
     * 添加数据库支持(默认不加载该Bean,有其它Bean引用时再加载)
     * @author ixx
     * @date 2019-07-05
     */
    public class JdbcTemplate {
    
        public int insert(String sql){
            return DbUtil.update(sql) ? 1 : 0;
        }
        public int insert(String sql, Object... params){
            return DbUtil.update(sql, params) ? 1 : 0;
        }
    
        public int update(String sql, Object... params){
            return DbUtil.update(sql, params) ? 1 : 0;
        }
        public int update(String sql){
            return DbUtil.update(sql) ? 1 : 0;
        }
    
        public int delete(String sql){
            return DbUtil.update(sql) ? 1 : 0;
        }
        public int delete(String sql, Object... params){
            return DbUtil.update(sql, params) ? 1 : 0;
        }
    
        public <T> List<T> select(String sql,Class<T> clazz, Object... param){
            return DbUtil.select(sql, clazz, param);
        }
    }
    
    

    修改依赖注入部分代码,因为做的是框架,要考虑不使用DB的情况,所以jdbcTemplate默认不初始化,如果有其它Service添加了JdbcTemplate的依赖,再去初始化。防止直接初始化时,没有配置DB相关配置报错的情况。

    BeansInitUtil.initDI()方法做如下修改(只保留了大体结构,具体查看=======标记中间的部分):

    private static void initDI(Map<String, BeanObject> map){
            List<Object> beanList = new ArrayList<>();
            BeanObject sqlBean = null;
            // 循环所有Bean处理依赖
            for(Map.Entry entry : map.entrySet()){
                // ...
                // 先判断是否有Resource注解
                for (Field field : beanObject.getFields()) {
                    if(filterFieldAnnotation(field.getAnnotations())){
                        String name = getResourceName(field.getAnnotations());
                        BeanObject bean = null;
                        // 有指定bean名字按指定去取
                        if(name != null && !name.equals("")){
                            bean = map.get(firstToLowerCase(name));
                        } else {
                            // ...
                            // 如果有next说明是有多个实现的接口,则要判断名字
                            if(bean != null && bean.getNext() != null){
                                // ...=============================注意下边这部分=============
                            } else if(fieldClass.getName().equals(JdbcTemplate.class.getName())){
                                // 如果是JdbcTemplate依赖,则初始化DbUtil并初始化及注入JdbcTemplate
                                if(sqlBean == null) {
                                    DbUtil.init();
                                    sqlBean = new BeanObject();
                                    sqlBean.setClass(JdbcTemplate.class);
                                    sqlBean.setObject(new JdbcTemplate());
                                }
                                bean = sqlBean;
                            }
                            // ...=============================注意上边这部分=============
                        }
                        if(bean == null){
                            // ...
                        }
                        // 注入依赖
                        // ...
                    }
                }
            }
            map.put(JdbcTemplate.class.getName(), sqlBean);
        }
    

    05、验证数据库支持

    表abc的表结构:

    字段名 字段类型 备注
    id int 自增id主健
    name varchar(255) 姓名

    修改DefImpl.java,添加JdbcTemplate操作:

    @Service
    public class DefImpl implements Def {
        @Resource
        private JdbcTemplate jdbcTemplate;
    
        @Override
        public String exe(String name) {
            List<AbcEntity> list = jdbcTemplate.select("select * from abc where name=?", AbcEntity.class, name);
            System.out.println(list.size());
            System.out.println(list.get(0).getId());
            System.out.println(list.get(0).getName());
            return "Interface DI... "+name;
        }
    }
    

    修改AbcImpl.java, 添加JdbcTemplate操作:

    @Service
    public class AbcImpl implements Abc {
        // 名字对不上会报异常
        @Resource
        private Def defImpl;
        // 名字对不上可以使用注解中指定bean名字的方式
        @Resource(name = "def2Impl")
        private Def defByName;
        // 添加jdbcTemplate依赖
        @Resource
        private JdbcTemplate jdbcTemplate;
        // 注入Class类实例
        @Resource
        private ClassDi classDi;
        @Override
        public int test(String name) {
            jdbcTemplate.insert("insert into abc(`name`) values('ixx')");
            System.out.println(defImpl.exe(name));
            System.out.println(defByName.exe(name));
            System.out.println(classDi.exe(name));
            return 0;
        }
    }
    

    添加AbcEntity.java,查询结果使用

    package com.jisuye.service;
    
    public class AbcEntity {
        private Integer id;
        private String name;
        // getter and setter...
    }
    

    原来的SquareApplication中有查看bean是否注入成功片段, 所以直接启动项目即可验证。

    public static void run(Class clzz, String[] args) {
            try {
                // ...
                //查看bean是否注入成功
                Abc abc = (Abc)(ApplicationContext.getBean("abcImpl").getObject());
                abc.test("ixx");
                // ...
            } catch (Exception e){
                // ...
            }
    }
    

    查看控制台输出:

    18:18:39.761 [main] INFO com.jisuye.core.SquareApplication - beans size is:11
    1
    1
    ixx
    Interface DI... ixx
    def2 ixx
    Class DI ixx
    

    查看数据库记录:

    id name
    1 ixx

    说明插入及查询成功。

    06、翻车时间

    数据库这还好,虽然没有打包在其实项目引用测试,这个等下一篇添加完web支持后一起验证。

    发现一个注入的问题,当时处理注入是循环的整个容器里的bean去做解析,但很明显,容器中的bean是多对一的关系,多个key对应的 都是同一个Bean 如果使用循环的方式,就至少会多一倍解析处理..这个确实不好接受,Bean的数量少还可以,多了会延长程序的启动时间,这是不能接受的。解决方式其实也简单,下一篇再处理一下吧。

    本篇代码地址: https://github.com/iuv/square/tree/square5

    本文作者: ixx
    本文链接: http://jianpage.com/2019/07/08/square5
    版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!

    相关文章

      网友评论

          本文标题:造一个方形的轮子5--数据库支持

          本文链接:https://www.haomeiwen.com/subject/ghoahctx.html