自己写一个JDBC持久化框架,各种知识运用起来~

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41376740/article/details/81877238

引言

作为一个初学jdbc的人,你一定会被大量的语句编码所烦恼,一个连接就得啪啪啪写上10多行语句就是单单的为了仅仅执行一条sql语句,然后从结果集中写上了大量的get方法,苦的一笔啊~今天看了我这个,你讲告别烦恼,以后只需要使用简简单单的一条语句就行,其它框架帮你搞定。ps其实就是dbUtils的简单自制版

所用知识

设计思路

设计结构:
这里写图片描述
设计图示:
这里写图片描述
所用依赖:

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.9</maven.compiler.source>
    <maven.compiler.target>1.9</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.10</version>
    </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>RELEASE</version>
          <scope>compile</scope>
      </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.12</version>
    </dependency>

    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.3</version>
    </dependency>
  </dependencies>

    <!--build中添加-->
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>*.properties</include>
        </includes>
      </resource>
    </resources>

整体代码

写在前面:这个框架有一个缺陷,就是bean的属性和数据库中的需要高度一致,看了下面的源代码,你会发现,这个是反射读取bean的fields然后再读resultSet结果集的。
数据表结构:
这里写图片描述
模型bean:

package bean;

import java.time.LocalDate;

public class Student {

    private int id;
    private String name;
    private LocalDate birthday;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getBirthday() {
        return birthday;
    }

    public void setBirthday(LocalDate birthday) {
        this.birthday = birthday;
    }
}

不难看出我们将数据库的表的字段名称和实体类的field设置成了一致,之所以使用LocalDate,是因为它是1.8新出的东西,操作日期,线程安全等好处都是比较强大的。可以参考我这篇文章:https://blog.csdn.net/qq_41376740/article/details/81292504

核心包:

核心包的内容主要有这样的几块

  • 静态代码块初始化数据连接池,从properties文件中直接读取数据库的配置。
  • 获取,释放连接。这里我抽取了重复的代码,用重载实现几种释放资源
  • update用来执行增删改的操作
  • query重载方法,实现单一和多个查找的功能
  • 由于两个query有相同的封装bean的方法,所以我抽取出来形成了rs2Bean,就是将结果集封装到bean里面
package Utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import handlers.Handler;
import org.apache.commons.beanutils.BeanUtils;

import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author 肖鑫
 */
public class JDBCUtils {

    private static DataSource dataSource = null;

    static {
        InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("dbconfig.properties");
        Properties properties = new Properties();
        try {
            properties.load(in);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @return 返回从连接池中拿的连接的连接
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }


    /**
     * 关闭连接,释放资源
     *
     * @param conn 使用的连接
     */
    private static void release(Connection conn) {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException("关闭conn时异常", e);
        }
    }

    private static void release(Statement st) {
        try {
            if (st != null) {
                st.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException("关闭statement时异常", e);
        }
    }

    private static void release(ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException("关闭resultSet时异常", e);
        }
    }


    private static void release(Connection conn, Statement st) {
        release(conn);
        release(st);
    }

    private static void release(Connection con, Statement st, ResultSet rs) {
        release(con);
        release(st);
        release(rs);
    }


    /**
     * 用于结果集rs->bean的转换
     *
     * @param rs    结果集
     * @param clazz 传递进去的bean,为了适应不同的bean
     * @param <T>   泛型参数
     * @return 返回结果集中封装后的实例化的对象
     * @throws Exception
     */
    public static <T> T rs2Bean(ResultSet rs, Class<T> clazz) throws Exception {
        Field[] fields = clazz.getDeclaredFields();
        return createBean(rs, fields, clazz);
    }

    /**
     * 将结果集中的多个数据全部封装成bean然后存储进list里面
     *
     * @param rs    结果集
     * @param clazz 传递进去的bean,为了适应不同的bean
     * @param <T>   泛型参数
     * @return list集合
     * @throws Exception
     */
    public static <T> List<T> rs2BeanList(ResultSet rs, Class<T> clazz) throws Exception {
        Field[] fields = clazz.getDeclaredFields();
        List<T> list = new ArrayList<>();
        while (rs.next()) {
            T bean = createBean(rs, fields, clazz);
            list.add(bean);
        }
        return list;
    }

    /**
     * rs->bean的转换,用的是反射,单独抽取出来,是避免while循环中多次getDeclaredFields
     * @throws Exception
     */
    private static <T> T createBean(ResultSet rs, Field[] fields, Class<T> clazz) throws Exception {
        T bean = clazz.getConstructor().newInstance();
        for (Field field : fields) {
            String name = field.getName();
            Object object = rs.getObject(name);
            if (object instanceof Date) {
                object = ((Date) object).toLocalDate();
            }
            BeanUtils.setProperty(bean, name, object);
        }
        return bean;
    }

    /**
     * @param sql       需要执行的sql语句
     * @param paras     预编译中的?参数组成的一个可变数组
     */
    public static void update(String sql, Object... paras) {
        Connection conn = null;
        PreparedStatement st = null;

        try {
            conn = JDBCUtils.getConnection();
            st = conn.prepareStatement(sql);
            for (int i = 0; i < paras.length; i++) {
                Object obj = paras[i];
                if (obj instanceof LocalDate) {
                    Date date = Date.valueOf((LocalDate) obj);
                    st.setObject(i + 1, date);
                    continue;
                }
                st.setObject(i + 1, obj);
            }
            st.executeUpdate();
        } catch (Exception e) {
            throw new RuntimeException("更新错误", e);
        } finally {
            JDBCUtils.release(conn, st);
        }
    }

    /**
     *实现单个搜索功能
     */
    public static <T> T query(String sql, Handler<T> handler, Object... params) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JDBCUtils.getConnection();
            st = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                st.setObject(i + 1, params[i]);
            }
            rs = st.executeQuery();
            return handler.handle(rs);
        } catch (Exception e) {
            throw new RuntimeException("查询异常", e);
        } finally {
            JDBCUtils.release(conn, st, rs);
        }
    }

    /**
     * 执行搜索全部的功能
     */
    public static <T> List<T> queryAll(String sql, Handler<List<T>> handler) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JDBCUtils.getConnection();
            st = conn.prepareStatement(sql);
            rs = st.executeQuery();
            return handler.handle(rs);
        } catch (Exception e) {
            throw new RuntimeException("查询异常", e);
        } finally {
            JDBCUtils.release(conn, st, rs);
        }
    }
}

使用策略模式,来实现多个handle行为

定义接口:

package handlers;

import java.sql.ResultSet;

public interface Handler<T> {

    T handle(ResultSet rs) throws Exception;
}

两个实现:

package handlers;

import Utils.JDBCUtils;

import java.sql.ResultSet;

public class BeanHandler<T> implements Handler<T> {

    private Class<T> clazz;

    public BeanHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public T handle(ResultSet rs) throws Exception {
        return rs.next() ? JDBCUtils.rs2Bean(rs, this.clazz) : null;
    }
}
package handlers;

import Utils.JDBCUtils;

import java.sql.ResultSet;
import java.util.List;

public class BeanListHandler<T> implements Handler<List<T>> {

    private Class<T> clazz;

    public BeanListHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public List<T> handle(ResultSet rs) throws Exception {
        if (rs == null) {
            return null;
        }
        List<T> list = JDBCUtils.rs2BeanList(rs, this.clazz);
        return list.size() == 0 ? null : list;
    }
}

这个使用的是策略模式,我们定义了一个接口约定了行为,对于这个行为我们可以用多个不同的类来执行不同的处理手段,这样我们只需要根据需求new出实现类的实例,就可以使用接口定义的方法使用不同的处理方法。这里使用的是两个,一个是将查询的单条数据封装成单个bean,一个是将查询的多条数据封装成一个list。

测试代码

    @Test
    public void testUpdate() {
        LocalDate date = LocalDate.now();
        LocalDate date1 = LocalDate.of(1997, 1, 31);
        String sql = "insert into student(name,birthday) values(?,?)";
        JDBCUtils.update(sql, "肖鑫", date);

        String sql2 = "update student set birthday=? where name=?";
        JDBCUtils.update(sql2, date1, "肖鑫");
    }

    @Test
    public void testQuery() {
        String sql = "select * from student where id=?";
        BeanHandler<Student> handler = new BeanHandler<>(Student.class);
        Student student = JDBCUtils.query(sql, handler, 1);
    }

    @Test
    public void testQueryALl() {
        String sql = "select * from student";
        BeanListHandler<Student> handler = new BeanListHandler<>(Student.class);
        List<Student> list = JDBCUtils.queryAll(sql, handler);
    }

测试结果:
QueryAll:
这里写图片描述

Query:
这里写图片描述

数据库
原来的数据是2018-08-20,完成了更新的操作,并且插入了数据.
这里写图片描述

总结

这次学习,是一次不小的提升,用到了泛型,反射,平时我很少用到泛型,写泛型的时候总是那么尴尬,不知道怎么写,到处报错,泛型类和泛型方法,泛型接口怎么写,算是好好复习了一下。运用到了实践当中,然后就是策略模式的运用,原来这就是策略,不同的处理类实现同一个类。妙哉妙哉~很满足,很过瘾

猜你喜欢

转载自blog.csdn.net/qq_41376740/article/details/81877238