ORM框架封装(四)之#{}解析

该博客介绍了一个自定义的ORM框架,通过Handler和RowMapper接口简化了JDBC的使用。Handler类包含了解析SQL、处理参数和结果集的方法,而RowMapper接口则用于将结果集转换为对象。这个框架支持基础类型、复杂对象和Map类型的参数,并能处理单条和多条查询。通过反射和策略模式,实现了动态SQL和结果集到对象的映射,降低了DAO层的复杂性。
摘要由CSDN通过智能技术生成
package src.orm;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 这是自己定义的一个规则
 * 提供策略的规则接口
 */
public interface RowMapper {

    //设计一个规则方法
    //将rs的信息组装成一个对象domain
    //是否需要参数  rs
    //是否需要返回值  <T>
    <T>T mapperRow(ResultSet rs) throws SQLException;

}

package src.orm;

import orm.SQLAndKey;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Handler {

    //============================
    //设计一个小弟方法---帮助我们解析SQL语句
    SQLAndKey parseSQL(String sql){
        //解析之前肯定需要两个变量
        StringBuilder newSQL = new StringBuilder();
        List<String> keyList = new ArrayList<>();
        //解析SQL
        //insert into student values(#{sid},#{sname},#{ssex},#{sage})
        while(true){
            //按照规定的结构来找寻索引位置
            int left = sql.indexOf("#{");
            int right = sql.indexOf("}");
            //判断两个索引的位置是否合法
            if(left!=-1 && right!=-1 && left<right){
                //找到一组#{key}
                newSQL.append(sql.substring(0,left));//截取左边的部分拼接到newSQL里
                newSQL.append("?");
                keyList.add(sql.substring(left+2,right));
            }else{//找不到
                newSQL.append(sql);
                break;
            }
            sql = sql.substring(right+1);
        }
        return new SQLAndKey(newSQL,keyList);
    }
    //----------------------------
    //设计一个小小弟--负责帮下面那个handlerParameter方法处理map集合的拼接
    private void setMap(PreparedStatement pstat, Object obj, List<String> keyList) throws SQLException {
        //按照keyList规定的顺序 从map中获取元素 让pstat拼接到SQL上
        //还原一下obj类型
        Map map = (Map)obj;
        for(int i=0;i<keyList.size();i++){
            pstat.setObject(i+1,map.get(keyList.get(i)));
        }
    }
    //设计一个小小弟--负责帮下面那个handlerParameter方法处理domain的拼接
    private void setDomain(PreparedStatement pstat,Object obj,List<String> keyList) throws SQLException, NoSuchFieldException, IllegalAccessException {
        //获取obj类型
        Class clazz = obj.getClass();
        //遍历keyList 规定了顺序
        for(int i=0;i<keyList.size();i++){
            String key = keyList.get(i);
            //去domain对象中获取key对应属性的值
            Field field = clazz.getDeclaredField(key);
            //直接操作私有属性啦
            field.setAccessible(true);
            //拼接
            //  Object obj = 对象;        field = clazz.getField();
            //  value = obj.getName();   value = field.get(对象);
            pstat.setObject(i+1,field.get(obj));
        }
    }
    //设计一个小弟方法---帮我们处理参数(SQL和对象中的值拼接在一起)
    //  参数pstat  Object  顺序? keyList<key>
    void handlerParameter(PreparedStatement pstat,Object obj,List<String> keyList) throws SQLException {
        //获取这个obj对象的Class
        Class clazz = obj.getClass();
        //clazz通常可以是什么类型
        //  1.基础类型  int-Integer  float-Float  String
        //  2.domain类型  Student  Atm  Teacher
        //  3.map类型
        if(clazz==int.class || clazz==Integer.class){
            pstat.setInt(1,(Integer)obj);
        }else if(clazz==float.class || clazz==Float.class){
            pstat.setFloat(1,(Float)obj);
        }else if(clazz==double.class || clazz==Double.class){
            pstat.setDouble(1,(Double)obj);
        }else if(clazz==String.class){
            pstat.setString(1,(String)obj);
        }else if(clazz.isArray()){
            //自己处理 数组循环  对不起我不支持
        }else{
            //认为只剩下两个可能
            //1.map
            if(obj instanceof Map){
                this.setMap(pstat,obj,keyList);
            }else{//domain
                try {
                    this.setDomain(pstat,obj,keyList);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //----------------------------
    //设计一个小小弟--负责帮下面那个handlerResult方法处理map返回值
    private Map getMap(ResultSet rs) throws SQLException {
        //创建map
        Map result = new HashMap<String,Object>();
        //获取rs中的全部信息(表格原来的列名 和 查询到的值)  Set<Map<列名,值>>
        ResultSetMetaData resultSetMetaData = rs.getMetaData();
        //遍历含有列的信息
        for(int i=1;i<=resultSetMetaData.getColumnCount();i++){
            //获取一个列名字
            String columnName = resultSetMetaData.getColumnName(i);
            //根据列名字获取rs中的值
            Object value = rs.getObject(columnName);
            result.put(columnName,value);
        }
        return result;
    }
    //设计一个小小弟--负责帮下面那个handlerResult方法处理domain返回值
    private Object getDomain(ResultSet rs,Class resultType) throws IllegalAccessException, InstantiationException, SQLException, NoSuchFieldException {
        Object obj = null;
        //通过Class反射创建对象
        obj = resultType.newInstance();
        //遍历rs
        ResultSetMetaData resultSetMetaData = rs.getMetaData();
        for(int i=1;i<=resultSetMetaData.getColumnCount();i++){
            //获取一个列名字
            String columnName = resultSetMetaData.getColumnName(i);
            //反射找domain对象中对应列名的哪个属性
            Field field =resultType.getDeclaredField(columnName);
            //操作私有属性
            field.setAccessible(true);
            field.set(obj,rs.getObject(columnName));
        }
        return obj;
    }
    //设计一个小弟方法---帮我们处理返回值(rs信息取出 组装成一个对象 基础类型 domain map)
    Object handlerResult(ResultSet rs,Class resultType) throws SQLException {
        Object result = null;//变量用来存储最终的返回值
        if(resultType==int.class || resultType==Integer.class){
            result = rs.getInt(1);
        }else if(resultType==float.class || resultType==Float.class){
            result = rs.getFloat(1);
        }else if(resultType==double.class || resultType==Double.class){
            result = rs.getDouble(1);
        }else if(resultType==String.class){
            result = rs.getString(1);
        }else{
            //在这认为是对象  map  domain
            if(resultType==Map.class){
                result = this.getMap(rs);
            }else{
                try {
                    result = this.getDomain(rs,resultType);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
    //============================
}

package src.orm;

import java.util.ArrayList;
import java.util.List;

/**
 * 这是我们自己定义的类
 * 是为了解析带有#{key}形式的SQL时候
 * 装载解析后的返回值的
 * 解析后的返回值有两个
 * 一个是带有问号形式的原来SQL
 * 一个是ArrayList集合 集合内存放好多key
 */
public class SQLAndKey {

    //属性 存放原先形式的sql 带问号的
    private StringBuilder sql = new StringBuilder();
    //属性 存放解析出来的那些#{key}
    private List<String> keyList = new ArrayList();

    //带参数的构造方法
    public SQLAndKey(StringBuilder sql,List<String> keyList){
        this.sql = sql;
        this.keyList = keyList;
    }

    public String getSQL(){
        return this.sql.toString();
    }
    public List<String> getKeyList(){
        return this.keyList;
    }

}

package src.orm;

import orm.Handler;
import orm.RowMapper;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 单独设计了一个类
 * 负责帮所有的DAO来做事
 * 以后只能在这个类的方法中见到JDBC的流程啦
 * 以后所有的DAO再也看不见JDBC啦
 * 以后的DAO只保留了一条SQL语句
 */
@SuppressWarnings("all")
public class SqlSession {

    private orm.Handler handler = new Handler();

    //面向配置文件开发
    //驱动类 账号 密码都存在文件中

    //单独设计一个小弟方法
    //负责处理所有的增删改操作
    //  参数  SQL , SQL语句上需要的那些问号值
    //  返回值?  void   int--->行数       原本对象形式的容器--->数组Object[] 集合
    //  insert into student values(?,?,?,?)
    //  values->Object[] {10,"zzt","男",18}
    public void update(String sql,Object... values){//容器目的存储好几个问号值的
        String className = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        try {
            //加载驱动
            Class.forName(className);
            //获取连接
            Connection conn = DriverManager.getConnection(url,username,password);
            //创建状态参数----预处理
            PreparedStatement pstat = conn.prepareStatement(sql);
            //给SQL问号赋值的过程
            //  赋值 几个 什么类型
            for(int i=0;i<values.length;i++){
                pstat.setObject(i+1,values[i]);
            }
            //执行操作啦
            pstat.executeUpdate();
            //关闭
            pstat.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //单独设计一个小弟方法
    //负责处理所有DAO的单条查询
    //  参数      SQL  Object[]
    //  返回值    <T> Object(Student Atm)
    public <T>T selectOne(String sql, orm.RowMapper rm, Object... values){
        Object obj = null;
        String className = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        try {
            //加载驱动
            Class.forName(className);
            //获取连接
            Connection conn = DriverManager.getConnection(url,username,password);
            //创建状态参数----预处理
            PreparedStatement pstat = conn.prepareStatement(sql);
            //给SQL问号赋值的过程
            //  赋值 几个 什么类型
            for(int i=0;i<values.length;i++){
                pstat.setObject(i+1,values[i]);
            }
            //-------------------------------------------
            //执行操作啦---查询
            ResultSet rs = pstat.executeQuery();
            if(rs.next()){
                //将rs中的数据取出来  存入一个新的容器里  domain 数组 集合
                //1.  rs数据取出来 存入一个新的容器--->domain  map
                //2.  策略模式----多态的时候  银行取钱  去银行取钱的流程固定  每一人执行结果不一样
                //              一个类负责制定流程(方法)  提供一个参数(接口)  真正传参数的时候具体子类
                //              public void test(接口 a){
                //                  1.
                //                  2.
                //                  3.不一样 a.方法();
                //                  4.
                //                  5.
                //              }
                //              Test t = new Test();
                //              t.test(a子类对象);  子类重写之后的效果
                //  将rs的信息拿出来  组装成一个对象
                obj = rm.mapperRow(rs);
                //3.  利用反射来完成
            }
            //关闭
            rs.close();//rs就可以关闭啦
            pstat.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)obj;
    }

    //设计一个小弟方法
    //负责处理所有DAO的多条查询
    //  参数
    //  返回值
    //  select * from student where ssex = ? and sage = ?
    //  values  男  18
    //  三行记录
    public <T> List<T> selectList(String sql, RowMapper rm, Object...values){
        List<T> list = new ArrayList();
        String className = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        try {
            //加载驱动
            Class.forName(className);
            //获取连接
            Connection conn = DriverManager.getConnection(url,username,password);
            //创建状态参数----预处理
            PreparedStatement pstat = conn.prepareStatement(sql);
            //给SQL问号赋值的过程
            //  赋值 几个 什么类型
            for(int i=0;i<values.length;i++){
                pstat.setObject(i+1,values[i]);
            }
            //-------------------------------------------
            //执行操作啦---查询
            ResultSet rs = pstat.executeQuery();
            while(rs.next()){
                //将rs中的数据取出来  存入一个新的容器里  domain 数组 集合
                //1.  rs数据取出来 存入一个新的容器--->domain  map
                //2.  策略模式----多态的时候  银行取钱  去银行取钱的流程固定  每一人执行结果不一样
                //              一个类负责制定流程(方法)  提供一个参数(接口)  真正传参数的时候具体子类
                //              public void test(接口 a){
                //                  1.
                //                  2.
                //                  3.不一样 a.方法();
                //                  4.
                //                  5.
                //              }
                //              Test t = new Test();
                //              t.test(a子类对象);  子类重写之后的效果
                //  将rs的信息拿出来  组装成一个对象
                T obj = (T)rm.mapperRow(rs);
                list.add(obj);
                //3.  利用反射来完成
            }
            //关闭
            rs.close();//rs就可以关闭啦
            pstat.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }



    //方案二(利用反射将上述的方法参数values处理一下)
    public void update(String sql,Object obj){
        //Object obj可以理解为是原来的那个domain
        //新增
        //insert into student values(#{sid},#{sname},#{ssex},#{sage})
        //values[]  sid sname ssex sage  按顺序的?
        //student对象  sid sname ssex sage属性---->属性值拼到对应的问号位置上?
        //如果用户是按照上面的SQL写过来的
        //我们需要做什么???
        // 1.解析SQL---> 要每一个#{key}  将#{key}替换回原来的?
        //      一条原来形式的sql语句     String sql
        //      一堆key  集合ArrayList  ArrayList<String>       对象
        SQLAndKey sqlAndKey = handler.parseSQL(sql);
        //执行JDBC的流程
        String className = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
        String username = "root";
        String password = "123456";
        try {
            //加载驱动
            Class.forName(className);
            //获取连接
            Connection conn = DriverManager.getConnection(url,username,password);
            //创建状态参数----预处理
            PreparedStatement pstat = conn.prepareStatement(sqlAndKey.getSQL());
            //给SQL问号赋值的过程
            //  赋值 几个 什么类型
            //  问号的值存储在一个对象里的obj  对象里面的值和SQL拼接在一起
            handler.handlerParameter(pstat,obj,sqlAndKey.getKeyList());
            //执行操作啦
            pstat.executeUpdate();
            //关闭
            pstat.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    //方案二(利用反射将selectOne方法处理一下  rm  values)
    public <T>T selectOne(String sql,Object obj,Class resultType){
        Object result = null;
        try {
            //1.解析sql--带#{key}
            SQLAndKey sqlAndKey = handler.parseSQL(sql);
            //2.获取连接
            String className = "com.mysql.jdbc.Driver";
            String url = "jdbc:mysql://localhost:3306/newtest?useSSL=false&characterEncoding=utf8";
            String username = "root";
            String password = "123456";
            //加载驱动
            Class.forName(className);
            //获取连接
            Connection conn = DriverManager.getConnection(url,username,password);
            //创建状态参数----预处理
            PreparedStatement pstat = conn.prepareStatement(sqlAndKey.getSQL());
            //拼接SQL和问号的值
            handler.handlerParameter(pstat,obj,sqlAndKey.getKeyList());
            //-------------------------------------------
            //执行操作啦---查询
            ResultSet rs = pstat.executeQuery();
            if(rs.next()){
                //组装对象 将rs里面的信息取出来 组装成一个对象
                result = handler.handlerResult(rs,resultType);
            }
            //关闭
            rs.close();//rs就可以关闭啦
            pstat.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T)result;
    }






}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值