硬核手写简易mybatis框架

2023-09-24 20:43:58 浏览数 (1)

简易框架功能介绍

搭建这个简易的框架是为了加深对mybatis的理解,功能不是全部实现的(也没有能力),所以这个简易的框架的功能只支持表字段都为varchar,pojo为String类型的,而且本框架只支持JDBC事务管理器,只支持非池化,功能的话只实现了插入,查询(单个数据不支持多个)。

创建模块导入相关pom依赖

使用dom4j去进行解析xml文件

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mybatis-xml-dom4j</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!--dom4j依赖-->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--jaxen依赖-->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.2.0</version>
        </dependency>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>
资源工具类

这个类用于读取指定配置文件的输入流,即读取resourse目录下的文件

代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class Resource {
    /**
     * 从类路径中获取配置文件输入输出流动
     */
    public static InputStream getResourcesAsStream(String path){
        return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
    }
}
sqlSessionFactoryBuilder工厂构造类

读取batis核心配置文件,工具SqlSessionFactory对象

build方法主要负责功能解析配置文件: 创建数据源对象 创建事物管理器对象 获取所有的SQL映射文件

代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder() {

    }

    /**
     * 获取SqlSessionFactory对象
     * 读取batis核心配置文件,工具SqlSessionFactory对象
     * @param inputStream 指向核心配置文件的输入流动
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream){
        /**
         * 主要负责功能解析配置文件
         *          创建数据源对象
         *          创建事物管理器对象
         *          获取所有的SQL映射文件
         *          封装到SqlSessionFactory对象中
         *
         */
        return null;
    }
}
SqlSessionFactory设计和MappedStatement的编写

这个类应该包含: 一个属性为事务管理器,对应执行sql语句的MappedStatement对象 JDBCTransaction属性 Map<String, MappedStatement>属性

MappedStatement:

代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class MapperStatement {
    private  String sqlId;
    private String resultType;
    private  String sql;
    private  String parameterType;
    private  String sqlType;
    
    public MapperStatement(String sqlId, String resultType, String sql, String parameterType, String sqlType) {
        this.sqlId = sqlId;
        this.resultType = resultType;
        this.sql = sql;
        this.parameterType = parameterType;
        this.sqlType = sqlType;
    }
    
    @Override
    public String toString() {
        return "MapperStatement{"  
                "sqlId='"   sqlId   '''  
                ", resultType='"   resultType   '''  
                ", sql='"   sql   '''  
                ", parameterType='"   parameterType   '''  
                ", sqlType='"   sqlType   '''  
                '}';
    }

    public String getSqlId() {
        return sqlId;
    }

    public void setSqlId(String sqlId) {
        this.sqlId = sqlId;
    }

    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;
    }

    public String getParameterType() {
        return parameterType;
    }

    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }

    public String getSqlType() {
        return sqlType;
    }

    public void setSqlType(String sqlType) {
        this.sqlType = sqlType;
    }


}
JDBCTransaction设计

我们知道mybatis的事务管理器类型有只能填MANAGED或者JDBC,在本框架中我们只实现最简单的JDBC事务管理器

● transactionManager:配置事务管理器 ○ type属性:指定事务管理器具体使用什么方式,可选值包括两个 ■ JDBC:使用JDBC原生的事务管理机制。底层原理:事务开启conn.setAutoCommit(false); ...处理业务...事务提交conn.commit(); ■ MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。

​编辑

事务管理器最好是定义一个接口,然后每一个具体的事务管理器都实现这个接口。

代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class JDBCTransaction implements TransactionManager{
    /**
     * 数据库连接对象
     */
    private  Connection connection;

    /**
     * 数据源对象
     */
    private DataSource dataSource;

    /**
     * 自动提交标记
     * true:自动提交
     * false:不自动提交
     */
    private  boolean autoCommit;
    
    /**
     * 构造事务管理器对象
     */
    public JDBCTransaction( DataSource dataSource,Connection connection) {
        this.connection = connection;
        this.dataSource = dataSource;
       
    }

    @Override
    public void commit() {
        try {
            connection.commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    @Override
    public void close() {
        try {
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    @Override
    public void openConnection() {
        try {
            Connection connection = dataSource.getConnection();
            this.connection.setAutoCommit(this.autoCommit);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        
    }

    @Override
    public Connection getConnection() {
        return connection;
    }
}
JDBC事务管理器实现类
代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class JDBCTransaction implements TransactionManager{
    @Override
    public void commit() {

    }

    @Override
    public void rollback() {

    }

    @Override
    public void close() {

    }

    @Override
    public void openConnection() {

    }

    @Override
    public Connection getConnection() {
        return null;
    }
}
UNPOOLEDDataSource数据源类设计

unpool即在本框架中不使用数据库连接池技术实现

代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class UNPOOLEDDataSource implements  javax.sql.DataSource{
    private String url;
    private String username;
    private String password;
    
    public UNPOOLEDDataSource(String driver,String url,String username,String password){
        try {
            Class.forName(driver);
            
        }catch (Exception e){
            e.printStackTrace();
        }
        this.url=url;
        this.username=username;
        this.password=password;
    }


    @Override
    public Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,username,password);
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}
SqlSessionFactory类完善
代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class SqlSessionFactory {
    private  TransactionManager transactionManager;
    private Map<String,MapperStatement> mapperStatements;

    public SqlSessionFactory(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
        this.transactionManager = transactionManager;
        this.mapperStatements = mapperStatements;
    }

    public TransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public Map<String, MapperStatement> getMapperStatements() {
        return mapperStatements;
    }

    public void setMapperStatements(Map<String, MapperStatement> mapperStatements) {
        this.mapperStatements = mapperStatements;
    }
}
SqlSessionFactoryBuilder中的build方法编写
代码语言:javascript复制
/**
 * @author 风轻云淡
 */
public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder() {

    }

    /**
     * 获取SqlSessionFactory对象
     * 读取batis核心配置文件,工具SqlSessionFactory对象
     * @param inputStream 指向核心配置文件的输入流动
     * @return
     */
    public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
        /**
         * 主要负责功能解析配置文件
         *          创建数据源对象
         *          创建事物管理器对象
         *          获取所有的SQL映射文件
         *          封装到SqlSessionFactory对象中
         *
         */
        Document document = new SAXReader().read(inputStream);
        Element environmentsElt = (Element) document.selectSingleNode("/configuration/environments");
        String defaultEnv = environmentsElt.attributeValue("default");
        //解析文件得出数据源对象
        Element dataSourceElt = environmentsElt.element("dataSource");
        DataSource dataSource= getDataSource(dataSourceElt);
        //解析文件得出事务管理器对象
        Element transactionManagerElt = environmentsElt.element("transactionManager");
        TransactionManager transactionManager =gettransactionManager(transactionManagerElt,dataSource);
        //解析文件得出sql映射对象
        Element mapperElt=environmentsElt.element("mappers");
        Map<String,MapperStatement> mapperStatementMap =getMapperStatements(mapperElt);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transactionManager, mapperStatementMap);
        return sqlSessionFactory;
    }

    private Map<String, MapperStatement> getMapperStatements(Element mapperElt) {
        Map<String,MapperStatement> mapperStatements=new HashMap<>();
        try {
            String resource = mapperElt.attributeValue("resource");
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(Resource.getResourcesAsStream(resource));
            Element mapper = (Element) document.selectSingleNode("/mapper");
            String namespace = mapper.attributeValue("namespace");
            mapper.elements().forEach(item->{
                String sqlId = item.attributeValue("id");
                String sql = item.getTextTrim();
                String parameterType = item.attributeValue("parameterType");
                String resultType = item.attributeValue("resultType");
                String sqlType = item.getName().toLowerCase();
                //封装对象
                MapperStatement mapperStatement = new MapperStatement(sqlId, resultType, sql, parameterType, sqlType);
                mapperStatements.put(namespace "." sqlId,mapperStatement);

            });

        }catch (Exception e){
            e.printStackTrace();
        }
        return mapperStatements;
    }

    private TransactionManager gettransactionManager(Element transactionManagerElt, DataSource dataSource) {
        String type = transactionManagerElt.attributeValue("type").toUpperCase();
        TransactionManager transactionManager=null;
        if("JDBC".equals(type)){
            transactionManager=new JDBCTransaction(dataSource,false);
        }else{
            try {
                throw  new Exception("本框架只支持JDBC事务管理");
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        return transactionManager;
    }

    private DataSource getDataSource(Element dataSourceElt)  {
        //获取所有数据源配置
        Map<String ,String> dataSourceMap=new HashMap<>();
        dataSourceElt.elements().forEach(item->{
            dataSourceMap.put(item.attributeValue("name"),item.attributeValue("value"));
        });
        String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
        DataSource dataSource=null;
        if("UNPOOLED".equals(dataSourceType)){
            dataSource=new UNPOOLEDDataSource(dataSourceMap.get("driver"),
                    dataSourceMap.get("url"),
                    dataSourceMap.get("username"),
                    dataSourceMap.get("password"));
        }else{
            try {
                throw new Exception("本框架只实现了UNPOOLED");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return dataSource;
    }
}
SqlSession编写
代码语言:javascript复制
public class SqlSession {
    TransactionManager transactionManager;
    Map<String, MapperStatement> mapperStatements;

    public SqlSession(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
        this.transactionManager = transactionManager;
        this.mapperStatements = mapperStatements;
    }
    public void commit(){
        try {
            transactionManager.getConnection().commit();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void rollback(){
        try {
            transactionManager.getConnection().rollback();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void close(){
        try {
            transactionManager.getConnection().close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
在SqlSessionFactory中添加openSession方法
代码语言:javascript复制
public SqlSession openSession(){
    transactionManager.openConnection();
    SqlSession sqlSession = new SqlSession(transactionManager, mappedStatements);
    return sqlSession;
}
编写SqlSession类中的insert方法
代码语言:javascript复制
  /**
     * 插入数据
     *
     * @param sqlId 要执行的sqlId
     * @param obj   插入的数据
     * @return
     */
    public int insert(String sqlId, Object obj) {
        MapperStatement godMappedStatement = mapperStatements.get(sqlId);
        Connection connection = transactionManager.getConnection();
        // 获取sql语句
        // insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
        String godbatisSql = godMappedStatement.getSql();
        // insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)
        String sql = godbatisSql.replaceAll("#\{[a-zA-Z0-9_\$]*}", "?");

        // 重点一步
        Map<Integer, String> map = new HashMap<>();
        int index = 1;
        while (godbatisSql.indexOf("#") >= 0) {
            int beginIndex = godbatisSql.indexOf("#")   2;
            int endIndex = godbatisSql.indexOf("}");
            map.put(index  , godbatisSql.substring(beginIndex, endIndex).trim());
            godbatisSql = godbatisSql.substring(endIndex   1);
        }

        final PreparedStatement ps;
        try {
            ps = connection.prepareStatement(sql);

            // 给?赋值
            map.forEach((k, v) -> {
                try {
                    // 获取java实体类的get方法名
                    String getMethodName = "get"   v.toUpperCase().charAt(0)   v.substring(1);
                    Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
                    ps.setString(k, getMethod.invoke(obj).toString());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            int count = ps.executeUpdate();
            ps.close();
            return count;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
编写SqlSession类中的selectOne方法
代码语言:javascript复制
   /**
     * 查询一个对象
     * @param sqlId
     * @param parameterObj
     * @return
     */
    public Object selectOne(String sqlId, Object parameterObj){
        MapperStatement godMappedStatement = mapperStatements.get(sqlId);
        Connection connection = transactionManager.getConnection();
        // 获取sql语句
        String godbatisSql = godMappedStatement.getSql();
        String sql = godbatisSql.replaceAll("#\{[a-zA-Z0-9_\$]*}", "?");
        // 执行sql
        PreparedStatement ps = null;
        ResultSet rs = null;
        Object obj = null;
        try {
            ps = connection.prepareStatement(sql);
            ps.setString(1, parameterObj.toString());
            rs = ps.executeQuery();
            if (rs.next()) {
                // 将结果集封装对象,通过反射
                String resultType = godMappedStatement.getResultType();
                Class<?> aClass = Class.forName(resultType);
                Constructor<?> con = aClass.getDeclaredConstructor();
                obj = con.newInstance();
                // 给对象obj属性赋值
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                for (int i = 1; i <= columnCount; i  ) {
                    String columnName = rsmd.getColumnName(i);
                    String setMethodName = "set"   columnName.toUpperCase().charAt(0)   columnName.substring(1);
                    Method setMethod = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());
                    setMethod.invoke(obj, rs.getString(columnName));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            try {
                ps.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return obj;
    }

​​​我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

0 人点赞