温馨提示×

温馨提示×

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

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

如何手写mybatis框架

发布时间:2021-07-20 17:47:16 来源:亿速云 阅读:182 作者:chen 栏目:大数据

如何手写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 框架。希望本文能对读者有所帮助,激发大家对框架设计和实现的兴趣。

向AI问一下细节

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

AI