手写mybatis

1、目录结构

在这里插入图片描述

2、概述

在这里插入图片描述

  1. mappedstatement
    记录mapper.xml的配置信息
  2. configuration
    记录dp.properties信息,和所有的mapper.xml信息(用hashmap记录
  3. sqlSessionFactory
    • 加载dp.properties文件,封装到configuration对象中。
    • 加载mapper.xml文件,加载到mappedstatement对象中。在把mappedstatement对象封装到configuration对象中的hashmap中。
    • 创建session对象。
  4. sqlsession
    sqlsession是一个接口,他的默认实现类是DefaultSqlSession类,他的作用是:对外提供访问的api(selectone/selectlist)。将请求转发给executor。
  5. Executor
    Executor是一个接口,他的默认的实现类是DefaultExecutor。拿到configuration类的配置信息连接数据库,执行代码。

3、实现

mappedstatement

记录mapper.xml的信息

package com.lzq.mybatis.conf;
/*
存储mypper.xml信息
 */
public class MappedStatement {
    private String namespace;
    private String sourceId;
    private String resultType;
    private String sql;

    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    public String getSourceId() {
        return sourceId;
    }

    public void setSourceId(String sourceId) {
        this.sourceId = sourceId;
    }

    public String getResultType() {
        return resultType;
    }

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

    public String getSql() {
        return sql;
    }

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

configuration

记录db.properties信息,用HashMap<String,MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();记录mappedstatement类,key为namespace+id。

package com.lzq.mybatis.conf;

import java.util.HashMap;

public class Configuration {
    private String jdbcDriver;
    private String jdbcUrl;
    private String userName;
    private String passWord;
    /*
    string:namespace + id
     */
    HashMap<String,MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();

    public String getJdbcDriver() {
        return jdbcDriver;
    }

    public void setJdbcDriver(String jdbcDriver) {
        this.jdbcDriver = jdbcDriver;
    }

    public String getJdbcUrl() {
        return jdbcUrl;
    }

    public void setJdbcUrl(String jdbcUrl) {
        this.jdbcUrl = jdbcUrl;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public HashMap<String, MappedStatement> getMappedStatements() {
        return mappedStatements;
    }

    public void setMappedStatements(HashMap<String, MappedStatement> mappedStatements) {
        this.mappedStatements = mappedStatements;
    }
}

SqlSessionFactory

加载dp.properties文件,封装到configuration对象中。
加载mapper.xml文件,加载到mappedstatement对象中。在把mappedstatement对象封装到configuration对象中的hashmap中。
创建session对象。

package com.lzq.mybatis.session;
import com.lzq.mybatis.conf.Configuration;
import com.lzq.mybatis.conf.MappedStatement;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;

//实例化过程中加载配置文件信息到Configuration对象中
//生产sqlSession
public class sqlSessionFactory {
    private Configuration configuration = new Configuration();
    public sqlSessionFactory() {
        //加载db.properties配置信息到Configuration中。
        loadDBInfo();
        //加载mapper.xml文件信息到Configuration中。
        loadMapperInfo();
    }
    public sqlSession openSession(){
        return new DefaultSqlSession(configuration);
    }



    //记录mapper.xml文件的位置
    public static final String MAPPER_CONFIG_LOCATION = "mappers";
    //记录数据库配置文件存放位置(db.properties)
    public static final String DB_CONFIG_FILE = "db.properties";

    private void loadDBInfo(){
        //加载db.properties
        InputStream dbIn = sqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
        Properties properties = new Properties();
        try{
            properties.load(dbIn);
        }catch (Exception e){
            e.printStackTrace();
        }
        //将db.properties文件信息读入configuration对象中
        configuration.setJdbcDriver(properties.get("jdbc.driver").toString());
        configuration.setJdbcUrl(properties.get("jdbc.url").toString());
        configuration.setUserName(properties.get("jdbc.username").toString());
        configuration.setPassWord(properties.get("jdbc.password").toString());
    }

    private void loadMapperInfo(){
        URL resource = null;
        resource = sqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
        File mappers = new File(resource.getFile());
        //遍历mapper.xml加载到conf文件夹
        if(mappers.isDirectory()){
            File[] listFiles = mappers.listFiles();
            for(File file : listFiles){
                loadMapperInfo(file);
            }
        }
    }

    //加载单独的一个mapper.xml
    private void loadMapperInfo(File file) {
        //通过SAXReader读取文件
        SAXReader saxReader = new SAXReader();
        //通过read方法读取一个文件转化成Document文件
        Document document = null;
        try{
            document = saxReader.read(file);
        }catch (Exception e){
            e.printStackTrace();
        }
        //获取根节点元素,mapper
        Element root = document.getRootElement();
        //获取namespace元素
        String namespace = root.attribute("namespace").getData().toString();
        //获取select子列表元素
        List<Element> selects = root.elements("select");
        for(Element element : selects){
            //将获取到的内容存放到MappedStatement对象中
            MappedStatement mappedStatement = new MappedStatement();
            String resultType = element.attribute("resultType").getData().toString();
            String id = element.attribute("id").getData().toString();
            String sql = element.getData().toString();
            String sourceId = namespace + "." + id;
            mappedStatement.setResultType(resultType);
            mappedStatement.setSql(sql);
            mappedStatement.setNamespace(namespace);
            mappedStatement.setSourceId(sourceId);
            configuration.getMappedStatements().put(sourceId,mappedStatement);
        }

    }


}

SqlSession

sqlsession是一个接口,他的默认实现类是DefaultSqlSession类,他的作用是:对外提供访问的api(selectone/selectlist)。将请求转发给

SqlSession

package com.lzq.mybatis.session;
import java.util.List;
public interface sqlSession {
    //查询多条记录 statement == sourceid == namespace + id
    public <E> List<E> selectList(String statement,Object parameters);
    //查询一条记录
    public <T> T selectOne(String statement,Object parameters);
    //通过动态代理实现面向接口的编程模型
    public <T> T getMapper(Class<T> type);
}

DefaultSqlSession

package com.lzq.mybatis.session;
import com.lzq.mybatis.binding.MapperProxy;
import com.lzq.mybatis.conf.Configuration;
import com.lzq.mybatis.conf.MappedStatement;
import com.lzq.mybatis.executor.DefaultExecutor;
import com.lzq.mybatis.executor.Executor;
import java.lang.reflect.Proxy;
import java.util.List;

//对外提供访问的api
//将请求转发给executor
public class DefaultSqlSession implements sqlSession{
    private final Configuration conf;
    private Executor executor;
    public DefaultSqlSession(Configuration conf) {
        this.conf = conf;
        executor = new DefaultExecutor(conf);
    }
    //statement == sourceid == namespace + id
    public <E> List<E> selectList(String statement,Object parameters){
        MappedStatement ms = conf.getMappedStatements().get(statement);
        return executor.query(ms,parameters);
    }

    //查询一条记录
    public <T> T selectOne(String statement, Object parameters) {
        List<Object> res = this.selectList(statement,parameters);
        if(res == null && res.size() == 0){
            return null;
        }else if(res.size() == 1){
            return (T) res.get(0);
        }else{
            throw new RuntimeException("查询有多条记录");
        }
    }

    //通过动态代理实现面向接口的编程模型
    public <T> T getMapper(Class<T> type){
        MapperProxy mp = new MapperProxy(this);
        return (T) Proxy.newProxyInstance(type.getClassLoader(),new Class[]{type},mp);
    }
}

Executor

Executor

package com.lzq.mybatis.executor;
import com.lzq.mybatis.conf.MappedStatement;
import java.util.List;

public interface Executor {
    public <E> List<E> query(MappedStatement ms, Object parameters);
}

DefaultExecutor

package com.lzq.mybatis.executor;
import com.lzq.mybatis.conf.Configuration;
import com.lzq.mybatis.conf.MappedStatement;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class DefaultExecutor implements Executor{
    private Configuration conf;
    public DefaultExecutor(Configuration configuration) {
        this.conf = configuration;
    }

    public <E> List<E> query(MappedStatement ms, Object parameters) {
        System.out.println(ms.getSql());
        //返回结果
        List<E> res = new ArrayList<E>();
        try{
            //加载驱动
            Class.forName(conf.getJdbcDriver());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try{
            //获取Connection连接,从conf中获取连接的信息。
            connection = DriverManager.getConnection(conf.getJdbcUrl(),conf.getUserName(),conf.getPassWord());
            //创建preparestatement信息,从mapperstatement中获取sql语句信息。
            preparedStatement = connection.prepareStatement(ms.getSql());
            //设置parameter
            handleParameters(preparedStatement,parameters);
            //执行sql获取resultSet
            resultSet = preparedStatement.executeQuery();

            Class domainClass = Class.forName(ms.getResultType());
            while (resultSet.next()) {
                //实例化要封装的实体类对象
                E obj = (E) domainClass.newInstance();
                //取出结果集的元信息:ResultSetMetaData
                ResultSetMetaData rsmd = resultSet.getMetaData();
                //取出总列数
                int columnCount = rsmd.getColumnCount();
                //遍历总列数
                for (int i = 1; i <= columnCount; i++) {
                    //获取每列的名称,列名的序号是从1开始的
                    String columnName = rsmd.getColumnName(i);
                    //根据得到列名,获取每列的值
                    Object columnValue = resultSet.getObject(columnName);
                    //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
                    //要求:实体类的属性和数据库表的列名保持一种
                    PropertyDescriptor pd = new PropertyDescriptor(columnName, domainClass);
                    //获取它的写入方法
                    Method writeMethod = pd.getWriteMethod();
                    //把获取的列的值,给对象赋值
                    writeMethod.invoke(obj, columnValue);
                }
                //把赋好值的对象加入到集合中
                res.add(obj);
            }
        }catch (Exception e){
            throw new RuntimeException(e);
        }finally {
            try{
                resultSet.close();
                preparedStatement.close();
                connection.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        return res;
    }

    //处理parameters
    public void handleParameters(PreparedStatement preparedStatement,Object parameter) throws SQLException {
        if(parameter instanceof Integer){
            preparedStatement.setInt(1, (Integer) parameter);
        }else if(parameter instanceof Long){
            preparedStatement.setLong(1, (Long) parameter);
        }else if(parameter instanceof String){
            preparedStatement.setString(1, (String) parameter);
        }
    }
}

动态代理

ibatis访问方式:

session.selectOne("com.lzq.mybatis.mapper.userMapper.selectById","1");
session.selectList("com.lzq.mybatis.mapper.userMapper.selectAll",null);

这样的访问方式参数太长了,不方便所以mybatis改成了面向接口的编程模型。采用动态代理的方式来代替以上的方式。通过传入的接口拿到类名和方法名 == namespace + id代替以上的第一个参数。

package com.lzq.mybatis.binding;
import com.lzq.mybatis.session.sqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;

public class MapperProxy implements InvocationHandler {
    private sqlSession session;
    public MapperProxy(sqlSession session) {
        this.session = session;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //如果是Collection的子类,那么就调用selectList否则就调用selectOne
        if(Collection.class.isAssignableFrom(method.getReturnType())){
            return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(),args == null?null:args[0]);
        }else{
            return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),args == null?null:args[0]);
        }
    }
}

测试

编写User类
package com.lzq.mybatis.entity;

public class User {
    private String id;
    private String name;
    private Integer age;

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

编写Mapper接口
package com.lzq.mybatis.mapper;
import com.lzq.mybatis.entity.User;
import java.util.List;

public interface userMapper {
    User selectById(String id);
    List<User> selectAll();
}

编写mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.lzq.mybatis.mapper.userMapper">
    <!--suppress MybatisXMapperXmlInspection -->
    <select id="selectById" resultType="com.lzq.mybatis.entity.User">
        select * from user where id = ?
    </select>
    <!--suppress MybatisXMapperXmlInspection -->
    <select id="selectAll" resultType="com.lzq.mybatis.entity.User">
        select * from user
    </select>
</mapper>
编写db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/crm_ssm?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
编写测试类
public class test {
    @Test
    public void test(){
        sqlSessionFactory factory = new sqlSessionFactory();
        sqlSession session = factory.openSession();
        System.out.println(session.toString());
        //通过动态代理实现面向接口的编程模式
        //session.selectOne("com.lzq.mybatis.mapper.userMapper.selectById","1");
       userMapper mapper = session.getMapper(userMapper.class);
        User user = mapper.selectById("2");
        System.out.println(user);
        //session.selectList("com.lzq.mybatis.mapper.userMapper.selectAll",null);
       /*List<User> selectAll = mapper.selectAll();
        for(User saleVisit : selectAll){
            System.out.println(saleVisit.toString());
        }*/
    }
}

结果

selectone
在这里插入图片描述
selectlist
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值