手撸极简mybatis

本文深入解析了MyBatis中的SessionFactory和Session实现,展示了如何通过工厂模式创建会话工厂,以及利用代理模式生成SQLHandler执行SQL操作。详细解释了数据源工厂DataSourceFactory、DaoWrapper类以及SQLHandler如何协同工作,实现了数据库的CRUD操作。同时,给出了测试用例和UserMapper.xml配置文件,帮助理解整个流程。
摘要由CSDN通过智能技术生成

请先熟知工厂模式和代理模式哦

core:

session工厂:

/**
 * 一个连接对应一个会话,所以创建会话工厂
 * 整合数据源,每次打开会话从数据源拿一个session
 */
public class SessionFactory {
   //要有连接session才能生效哦
    private DataSource dataSource;

    /**
     *  把UserMapper的配置文件加载进来形成env,有需要的就从env中拿
     *  第一个String:namespace; 第二个String:方法名(saveUser,select..);
     *  DaoWrapper:拿到方法名标签中所有的内容比如resultype,paramType,sql语句...
     */
    private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);

    //拿mybatis配置文件的路径,需要dom4j解析xml文件
    public SessionFactory(String config) {
        loadXml(config);
    }

    // 打开一个会话
    public Session openSession() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();   //session拿到连接才能建立会话
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return new Session(connection, env);
    }

    //   用dom4j依赖包  加载资源环境  解析数据源
    public void loadXml(String config) {
        try {
            //解析xml文件
            SAXReader reader = new SAXReader();
            Document configDom = reader.read(Session.class.getClassLoader().getResourceAsStream(config));
            Element configRoot = configDom.getRootElement();//拿到根节点
            String dataSourceType = configRoot.element("dataSource").getTextTrim();
            //赋值datasource
            dataSource = DataSourceFactory.createDataSource(dataSourceType);

            //获取所有的mapper文件
            List mapperElements = configRoot.elements("mapper");
            List<String> mapperPaths = new ArrayList<>();
            for (Object element : mapperElements) {
                Element mapper = (Element) element;
                mapperPaths.add(mapper.getTextTrim());
            }

            for (String mapperPath : mapperPaths) {
//                String:xml文件的方法名id,DaoWrapper:DaoWrapper中的成员
                Map<String, DaoWrapper> wrapper = new HashMap<>(2);
                Document document = reader.read(Session.class.getClassLoader().getResourceAsStream(mapperPath));
                Element root = document.getRootElement();
                String namespace = root.attribute("namespace").getValue();
                Iterator iterator = root.elementIterator();
                while (iterator.hasNext()) {
                    Element el = (Element) iterator.next();
                    String type = el.getName();  //什么样的sql语句,insert update or...
                    String id = el.attribute("id").getValue();
                    String resultType = el.attribute("resultType").getValue();
                    String paramType = el.attribute("paramType").getValue();
                    String sqlStr = el.getTextTrim(); //具体sql语句

//                    id是方法名,
                    wrapper.put(id, new DaoWrapper(type, resultType, paramType, sqlStr));
                }
                //xml文件里的namespace
                env.put(namespace, wrapper);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }

    }
}

session:

**
 * 通过session拿到一个代理对象,并操作会话
 * 在session里用dao生成代理对象
 * session就是客户端和服务端建立的会话,通过会话可以通过connection发sql
 * 一个会话有一个connection,connection从数据源中来
 */
public class Session {
    /**
     * 每个会话持有一个链接
     */
    private Connection connection;

    /**
     * 当前会话的上下文
     */
    private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);

    public Session(Connection connection,Map<String, Map<String, DaoWrapper>> env) {
        this.connection = connection;
        this.env = env;
    }

    /**
     * 拿到一个包装类xxxWrapper(代理对象)
     * @param clazz:也就是UserDao,传进来的代理对象。clazz.getName()就是拿到com.itnls.UserDao
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> clazz) {
        T t = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[]{clazz},
                new SQLHandler(connection, clazz,env.get(clazz.getName())));
        return t;
    }

    // 开始会话
    public void begin() {
        try {
            connection.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 提交
    public void commit() {
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    // 回滚
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection() {
        return connection;
    }

}

sql代理对象:

/**代理对象执行指定sql
 *根据xml内容和接口对应的名字,执行相应实际的方法体
 */
public class SQLHandler implements InvocationHandler {

    /**
     * 需传入一个链接
     */
    private Connection connection;
    /**
     * 需传入一个dao的类型
     */
    private Class clazz;
    /**
     * 需传入一个独立的小环境,此时已经在namespace中了,就不用大Map套小map了
     */
    private Map<String, DaoWrapper> env;


    public SQLHandler(Connection connection, Class clazz, Map<String, DaoWrapper> env) {
        this.connection = connection;
        this.clazz = clazz;
        this.env = env;
    }

    /**
     * 生成代理DaoWrapper对象
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  // 根据方法名拿到包装,包装里有sql语句,参数类型,返回值类型,type(insert,select...)
        DaoWrapper wrapper = env.get(method.getName());
//    预编译语句
        PreparedStatement statement = connection.prepareStatement(wrapper.getSql());

        // 对每一种sql语句进行独立的操作
        if ("insert".equals(wrapper.getType())) {
            String paramType = wrapper.getParamType();
            // 暂定传入一个对象
            Class<?> clazz = args[0].getClass();
            Field[] fields = clazz.getDeclaredFields();
//            遍历所有字段
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                statement.setObject(i + 1, fields[i].get(args[0]));
            }
            return statement.executeUpdate();

        } else if ("delete".equals(wrapper.getType())) {
            for (int i = 0; i < args.length; i++) {
                statement.setObject(i + 1, args[i]);
            }
            return statement.executeUpdate();
//args是代理对象传进的实参
        } else if ("select".equals(wrapper.getType())) {
            for (int i = 0; i < args.length; i++) {
                statement.setObject(i + 1, args[i]);
            }
            ResultSet result = statement.executeQuery();
            List list = new ArrayList();
            while (result.next()) {
                Class<?> clazz = Class.forName(wrapper.getResultType());
                Object object = clazz.newInstance();
                Field[] fields = clazz.getDeclaredFields();
                for (int i = 0; i < fields.length; i++) {
                    fields[i].setAccessible(true);
                    fields[i].set(object, result.getObject(fields[i].getName()));

                }
                list.add(object);
            }
            return list;
        }
        return null;
    }
}

DaoWrapper:

/**
 * 对应xxxMapper.xml的类  用于描述一个Dao的方法的必要条件
 */
public class DaoWrapper implements Serializable {
    /**
     * sql语句类型,insert|update|delete
     */
    private String type;
    /**
     * 返回值的类型
     */
    private String resultType;
    /**
     * 参数的类型
     */
    private String paramType;
    /**
     * sql语句
     */
    private String sql;

    public DaoWrapper(String type, String resultType, String paramType, String sql) {
        this.type = type;
        this.resultType = resultType;
        this.paramType = paramType;
        this.sql = sql;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }

    public String getParamType() {
        return paramType;
    }

    public void setParamType(String paramType) {
        this.paramType = paramType;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    @Override
    public String toString() {
        return "DaoWrapper{" +
                "type='" + type + '\'' +
                ", resultType='" + resultType + '\'' +
                ", paramType='" + paramType + '\'' +
                ", sql='" + sql + '\'' +
                '}';
    }
}

数据源工厂

public class DataSourceFactory {

    //不要直接在方法体内部写双引号的字符串内容  阿里规约这么写:
    public static final String DATASOURCE_TYPE_HIKARI = "hikari";
    public static final String DATASOURCE_TYPE_DRUID = "druid";

    public static DataSource createDataSource(String type){

        DataSource dataSource = null;
        Properties properties = new Properties();
        if(DATASOURCE_TYPE_HIKARI.equals(type)){
            try {
                properties.load(DataSourceFactory.class.getClassLoader().getResourceAsStream("hikari.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            HikariConfig hikariConfig = new HikariConfig(properties);
            dataSource = new HikariDataSource(hikariConfig);

        } else if (DATASOURCE_TYPE_DRUID.equals(type)){
            try {
                properties.load(DataSourceFactory.class.getClassLoader().getResourceAsStream("druid.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.configFromPropety(properties);
            dataSource = druidDataSource;
        }

        return dataSource;
    }

}

人承担的工作:

测试用例:

public class Test {
    public static void main(String[] args) {
        SessionFactory sessionFactory = new SessionFactory("mybatis-config.xml");
        Session session = sessionFactory.openSession();
        UserDao userDao=session.getMapper(UserDao.class);  //代理
        userDao.saveUser(new User(7,"al","bk"));
    }
}

UserMapper.xml:

<mapper namespace="com.itnls.dao.UserDao">
    <insert id="saveUser" resultType="java.lang.Integer" paramType="com.itnls.entity.User">
        insert into user_nls values(?,?,?)
    </insert>
    <select id="findUser" resultType="com.itnls.entity.User" paramType="java.lang.Integer">
        select * from user_nls where id = ?
    </select>
</mapper>

UserDao:

public interface UserDao {
    Integer saveUser(User user);

    List<User> findUser(Integer id);
}

mybatis-config.xml:

<mybatis>
<dataSource>druid</dataSource>
<mapper>mapper/userMapper.xml</mapper>
</mybatis>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值