您好,登录后才能下订单哦!
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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。