如何手写mybatis框架

发布时间:2021-07-20 17:47:16 作者:chen
来源:亿速云 阅读:162

如何手写MyBatis框架

引言

MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

本文将详细介绍如何手写一个简化版的 MyBatis 框架,帮助读者深入理解 MyBatis 的工作原理和实现机制。

1. MyBatis 框架的核心组件

在开始手写 MyBatis 框架之前,我们需要了解 MyBatis 的核心组件及其功能:

  1. SqlSessionFactory: 用于创建 SqlSession 的工厂类。
  2. SqlSession: 用于执行 SQL 命令、获取映射器和管理事务。
  3. Executor: 执行器,负责 SQL 语句的执行。
  4. MappedStatement: 封装了 SQL 语句、输入参数、输出结果等信息。
  5. Configuration: 配置类,包含了 MyBatis 的所有配置信息。
  6. MapperProxy: 动态代理类,用于生成 Mapper 接口的代理对象。

2. 手写 MyBatis 框架的步骤

2.1 创建 Configuration 类

Configuration 类是 MyBatis 的核心配置类,它包含了所有的配置信息,如数据源、Mapper 接口、SQL 语句等。

public class Configuration {

    private DataSource dataSource;
    private Map<String, MappedStatement> mappedStatements = new HashMap<>();

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public MappedStatement getMappedStatement(String statementId) {
        return mappedStatements.get(statementId);
    }

    public void addMappedStatement(String statementId, MappedStatement mappedStatement) {
        this.mappedStatements.put(statementId, mappedStatement);
    }
}

2.2 创建 MappedStatement 类

MappedStatement 类用于封装 SQL 语句、输入参数、输出结果等信息。

public class MappedStatement {

    private String id;
    private String sql;
    private Class<?> parameterType;
    private Class<?> resultType;

    public String getId() {
        return id;
    }

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

    public String getSql() {
        return sql;
    }

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

    public Class<?> getParameterType() {
        return parameterType;
    }

    public void setParameterType(Class<?> parameterType) {
        this.parameterType = parameterType;
    }

    public Class<?> getResultType() {
        return resultType;
    }

    public void setResultType(Class<?> resultType) {
        this.resultType = resultType;
    }
}

2.3 创建 SqlSessionFactory 类

SqlSessionFactory 是用于创建 SqlSession 的工厂类。

public class SqlSessionFactory {

    private Configuration configuration;

    public SqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}

2.4 创建 SqlSession 接口及其实现类

SqlSession 是 MyBatis 的核心接口,用于执行 SQL 命令、获取映射器和管理事务。

public interface SqlSession {

    <T> T selectOne(String statementId, Object parameter);

    <T> List<T> selectList(String statementId, Object parameter);

    <T> T getMapper(Class<T> type);
}

DefaultSqlSessionSqlSession 的默认实现类。

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    private Executor executor;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
        this.executor = new SimpleExecutor(configuration);
    }

    @Override
    public <T> T selectOne(String statementId, Object parameter) {
        List<T> list = selectList(statementId, parameter);
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.size() > 1) {
            throw new RuntimeException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
    }

    @Override
    public <T> List<T> selectList(String statementId, Object parameter) {
        MappedStatement mappedStatement = configuration.getMappedStatement(statementId);
        return executor.query(mappedStatement, parameter);
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type, this);
    }
}

2.5 创建 Executor 接口及其实现类

Executor 是执行器接口,负责 SQL 语句的执行。

public interface Executor {

    <T> List<T> query(MappedStatement mappedStatement, Object parameter);
}

SimpleExecutorExecutor 的简单实现类。

public class SimpleExecutor implements Executor {

    private Configuration configuration;

    public SimpleExecutor(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <T> List<T> query(MappedStatement mappedStatement, Object parameter) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = configuration.getDataSource().getConnection();
            preparedStatement = connection.prepareStatement(mappedStatement.getSql());
            if (parameter != null) {
                preparedStatement.setObject(1, parameter);
            }
            resultSet = preparedStatement.executeQuery();
            return resultSetToObject(resultSet, mappedStatement.getResultType());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private <T> List<T> resultSetToObject(ResultSet resultSet, Class<T> resultType) {
        List<T> result = new ArrayList<>();
        try {
            while (resultSet.next()) {
                T obj = resultType.newInstance();
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(columnName);
                    Field field = resultType.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(obj, value);
                }
                result.add(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }
}

2.6 创建 MapperProxy 类

MapperProxy 是动态代理类,用于生成 Mapper 接口的代理对象。

public class MapperProxy<T> implements InvocationHandler {

    private SqlSession sqlSession;
    private Class<T> mapperInterface;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String statementId = mapperInterface.getName() + "." + method.getName();
        MappedStatement mappedStatement = sqlSession.getConfiguration().getMappedStatement(statementId);
        if (mappedStatement == null) {
            throw new RuntimeException("Statement not found: " + statementId);
        }
        return sqlSession.selectOne(statementId, args[0]);
    }
}

2.7 完善 Configuration 类

Configuration 类中添加 getMapper 方法,用于获取 Mapper 接口的代理对象。

public class Configuration {

    private DataSource dataSource;
    private Map<String, MappedStatement> mappedStatements = new HashMap<>();

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public MappedStatement getMappedStatement(String statementId) {
        return mappedStatements.get(statementId);
    }

    public void addMappedStatement(String statementId, MappedStatement mappedStatement) {
        this.mappedStatements.put(statementId, mappedStatement);
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new MapperProxy<>(sqlSession, type));
    }
}

2.8 测试手写的 MyBatis 框架

现在,我们已经完成了手写 MyBatis 框架的核心组件。接下来,我们可以编写一个简单的测试用例来验证框架的功能。

public class MyBatisTest {

    public static void main(String[] args) {
        // 创建数据源
        DataSource dataSource = new SimpleDataSource("jdbc:mysql://localhost:3306/test", "root", "password");

        // 创建 Configuration
        Configuration configuration = new Configuration();
        configuration.setDataSource(dataSource);

        // 添加 MappedStatement
        MappedStatement mappedStatement = new MappedStatement();
        mappedStatement.setId("com.example.UserMapper.selectUserById");
        mappedStatement.setSql("SELECT * FROM user WHERE id = ?");
        mappedStatement.setParameterType(Integer.class);
        mappedStatement.setResultType(User.class);
        configuration.addMappedStatement("com.example.UserMapper.selectUserById", mappedStatement);

        // 创建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(configuration);

        // 创建 SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 获取 Mapper 接口
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 执行查询
        User user = userMapper.selectUserById(1);
        System.out.println(user);
    }
}

2.9 编写 Mapper 接口

public interface UserMapper {

    User selectUserById(Integer id);
}

2.10 编写 User 类

public class User {

    private Integer id;
    private String name;
    private Integer age;

    // getters and setters
}

3. 总结

通过以上步骤,我们成功手写了一个简化版的 MyBatis 框架。虽然这个框架的功能远不及真正的 MyBatis 强大,但它已经具备了 MyBatis 的核心功能,如 SQL 映射、动态代理、结果集映射等。

通过手写 MyBatis 框架,我们不仅加深了对 MyBatis 工作原理的理解,还掌握了如何设计和实现一个简单的 ORM 框架。希望本文能对读者有所帮助,激发大家对框架设计和实现的兴趣。

推荐阅读:
  1. Mybatis框架(一)初识Mybatis框架
  2. MyBatis框架介绍及实战操作

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mybatis

上一篇:java中BitCaskLock的使用方法

下一篇:怎么修改gazebo物理参数

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》