手写简单版mybatis
时间: 2023-06-19 17:06:34 浏览: 135
手写mybatis
Mybatis是一款优秀的持久层框架,它封装了JDBC操作数据库的细节,使得开发者只需要关注SQL语句编写即可,同时还能够有效的防止SQL注入等问题。下面我来手写一个简单版的Mybatis框架。
1. 定义实体类
首先我们需要定义实体类,这里以一个User实体类为例:
```java
public class User {
private Integer id;
private String name;
private Integer age;
// 省略getter和setter方法
}
```
2. 定义Mapper接口
接着我们需要定义Mapper接口,用于定义SQL语句和参数映射关系。这里同样以User为例:
```java
public interface UserMapper {
User selectById(Integer id);
List<User> selectAll();
void insert(User user);
void update(User user);
void deleteById(Integer id);
}
```
3. 编写配置文件
接下来需要编写配置文件,定义数据库连接信息、Mapper接口和SQL语句对应关系。配置文件通常包含两部分:
- 数据库连接信息:
```xml
<datasource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/test" username="root" password="root"/>
```
- Mapper接口和SQL语句对应关系:
```xml
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="com.example.entity.User">
select * from user where id=#{id}
</select>
<select id="selectAll" resultType="com.example.entity.User">
select * from user
</select>
<insert id="insert">
insert into user(name,age) values(#{name},#{age})
</insert>
<update id="update">
update user set name=#{name},age=#{age} where id=#{id}
</update>
<delete id="deleteById">
delete from user where id=#{id}
</delete>
</mapper>
```
4. 编写SqlSession类
SqlSession是Mybatis的核心类,用于创建和管理数据库连接、执行SQL语句、封装结果集等操作。这里我们先定义一个简单的SqlSession类:
```java
public class SqlSession {
private Configuration configuration;
private Connection connection;
public SqlSession(Configuration configuration) {
this.configuration = configuration;
}
public <T> T getMapper(Class<T> clazz) {
return configuration.getMapper(clazz, this);
}
public Connection getConnection() {
try {
if (connection == null || connection.isClosed()) {
connection = DriverManager.getConnection(configuration.getDataSource().getUrl(),
configuration.getDataSource().getUsername(),
configuration.getDataSource().getPassword());
}
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
```
5. 编写Configuration类
Configuration类用于解析和管理配置文件,创建SqlSession和Mapper接口实例。这里我们主要关注两个方法getMapper和getMappedStatement:
```java
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 <T> T getMapper(Class<T> clazz, SqlSession sqlSession) {
return MapperProxyFactory.getMapperProxy(sqlSession, clazz);
}
public MappedStatement getMappedStatement(String statementId) {
return mappedStatements.get(statementId);
}
public void addMappedStatement(String statementId, MappedStatement mappedStatement) {
mappedStatements.put(statementId, mappedStatement);
}
}
```
6. 编写MappedStatement类
MappedStatement类用于封装SQL语句和参数映射信息,包括SQL语句、参数类型、返回值类型等。
```java
public class MappedStatement {
private String statementId;
private String sql;
private Class<?> parameterType;
private Class<?> resultType;
public MappedStatement(String statementId, String sql, Class<?> parameterType, Class<?> resultType) {
this.statementId = statementId;
this.sql = sql;
this.parameterType = parameterType;
this.resultType = resultType;
}
public String getStatementId() {
return statementId;
}
public void setStatementId(String statementId) {
this.statementId = statementId;
}
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;
}
}
```
7. 编写MapperProxyFactory类
MapperProxyFactory类用于创建Mapper接口的代理对象。在代理对象中我们可以根据Mapper接口的方法名获取对应的SQL语句和参数映射信息,并执行SQL语句。
```java
public class MapperProxyFactory<T> {
public static <T> T getMapperProxy(SqlSession sqlSession, Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
new MapperProxy(sqlSession, clazz));
}
}
```
8. 编写MapperProxy类
MapperProxy类是Mapper接口的代理对象,用于执行SQL语句。
```java
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 {
// 获取Mapper接口方法对应的MappedStatement
String statementId = mapperInterface.getName() + "." + method.getName();
MappedStatement mappedStatement = sqlSession.getConfiguration().getMappedStatement(statementId);
// 获取SQL语句和参数映射信息
String sql = mappedStatement.getSql();
Class<?> parameterType = mappedStatement.getParameterType();
Class<?> resultType = mappedStatement.getResultType();
// 封装SQL参数
Object param = null;
if (args != null && args.length == 1) {
param = args[0];
}
// 执行SQL语句
Connection connection = sqlSession.getConnection();
PreparedStatement pstmt = connection.prepareStatement(sql);
if (param != null) {
pstmt.setObject(1, param);
}
ResultSet rs = pstmt.executeQuery();
// 封装结果集
List<Object> resultList = new ArrayList<>();
while (rs.next()) {
Object result = resultType.newInstance();
Field[] fields = resultType.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
field.set(result, rs.getObject(field.getName()));
}
resultList.add(result);
}
// 返回结果
if (method.getReturnType() == List.class) {
return resultList;
} else {
return resultList.get(0);
}
}
}
```
到此为止,我们已经手写了一个简单版的Mybatis框架。当然,这只是一个简单的示例,实际上Mybatis还有很多复杂的功能和特性,比如动态SQL、缓存、插件等。
阅读全文