您好,登录后才能下订单哦!
MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
本文将详细介绍如何手写一个简化版的 MyBatis 框架,帮助读者深入理解 MyBatis 的工作原理和实现机制。
在开始手写 MyBatis 框架之前,我们需要了解 MyBatis 的核心组件及其功能:
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);
    }
}
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;
    }
}
SqlSessionFactory 是用于创建 SqlSession 的工厂类。
public class SqlSessionFactory {
    private Configuration configuration;
    public SqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}
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);
}
DefaultSqlSession 是 SqlSession 的默认实现类。
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);
    }
}
Executor 是执行器接口,负责 SQL 语句的执行。
public interface Executor {
    <T> List<T> query(MappedStatement mappedStatement, Object parameter);
}
SimpleExecutor 是 Executor 的简单实现类。
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;
    }
}
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]);
    }
}
在 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));
    }
}
现在,我们已经完成了手写 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);
    }
}
public interface UserMapper {
    User selectUserById(Integer id);
}
public class User {
    private Integer id;
    private String name;
    private Integer age;
    // getters and setters
}
通过以上步骤,我们成功手写了一个简化版的 MyBatis 框架。虽然这个框架的功能远不及真正的 MyBatis 强大,但它已经具备了 MyBatis 的核心功能,如 SQL 映射、动态代理、结果集映射等。
通过手写 MyBatis 框架,我们不仅加深了对 MyBatis 工作原理的理解,还掌握了如何设计和实现一个简单的 ORM 框架。希望本文能对读者有所帮助,激发大家对框架设计和实现的兴趣。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。