关于java:手写Mybatis框架

45次阅读

共计 7974 个字符,预计需要花费 20 分钟才能阅读完成。

剖析 JDBC 操作问题

问题总结

  1. 数据库连贯创立、开释频繁造成系统资源节约,从而影响零碎性能。
  2. Sql 语句在代码中硬编码,造成代码不易保护,理论利用中 sql 变动的可能较大,sql 变动须要扭转 java 代码。
  3. 应用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不肯定,可能 多也可能少,批改 sql 还要批改代码,零碎不易保护。
  4. 对后果集解析存在硬编码 (查问列名),sql 变动导致解析代码变动,零碎不易保护,如果能将数据 库 记录封装成 pojo 对象解析比拟不便

解决方案

  1. 应用数据库连接池初始化连贯资源
  2. 将数据库连贯配置、sql 语句抽取到 xml 配置文件中,使其满足开闭准则
  3. 应用反射、内省等底层技术,主动将实体与表进行属性与字段的主动映射

自定义框架设计思路

架构设计图

设计思路

应用端(提供配置)

  1. 应用 sqlMapperConfig.xml 提供全局配置信息(数据源等)
  2. 应用 Mapper.xml 提供 sql 语句文件信息
  3. 提供 SqlSession 接口类实现 select、insert、update、delete 操作

框架端(JDBC 封装)

  1. 读取配置文件

    • 读取 sqlMapperconfig.xml
    • 读取 Mapper.xml
  2. 解析配置文件

    • 解析文件生成 Configuration、MapperStatement 对象
  3. 执行 JDBC 流程

自定义框架实现

应用端

创立配置文件

sqlMapperConfig.xml(全局配置文件,包含数据源配置,mapper 门路等)

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<!-- 数据源配置 -->

<datasource>

<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>

<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>

<property name="username" value="root"/>

<property name="password" value=""/>

</datasource>

<!-- 引入 mapper.xml 文件门路 -->

<mapper resource="mapper/userMapper.xml"/>

</configuration>

mapper.xml(namespace 命名空间、SQL 语句配置等)

<?xml version="1.0" encoding="utf-8" ?>

<mapper namespace="com.gigabytc.dao.UserDao">

<select id="findAll" resultType="com.gigabytc.entity.User" >

select * from user

</select>

<select id="findByCondition" resultType="com.gigabytc.entity.User" parameterType="com.gigabytc.entity.User">

select * from user where id = #{id} and username = #{username}

</select>

<insert id="addUser" parameterType="com.gigabytc.entity.User" resultType="int">

insert into user values(#{id}, #{username}, #{password}, #{birthday})

</insert>

<update id="updateUser" parameterType="com.gigabytc.entity.User" resultType="int" >

update user SET username = #{username}, password=#{password}, birthday=#{birthday} WHERE id = #{id}

</update>

<delete id="deleteUser" parameterType="com.gigabytc.entity.User" resultType="int" >

delete from user where id=#{id}

</delete>

</mapper>

应用 sqlSession 操作接口(读取配置文件构建 SqlSession 数据库操作)

InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapperConfig.xml");

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

sqlSession = sqlSessionFactory.openSession();

List<User> list = sqlSession.selectList("user.findAll");

框架端

解析配置文件

Configuration(全局配置)

@Data
public class Configuration {

/**
- 数据源
*/
private DataSource dataSource;

/**
- SQL statement 汇合
*/
private Map<String, MapperStatement> mapperStatementMap = new HashMap<>();}

MapperStatement(SQL 语句配置)

@Data
public class MapperStatement {

/**
    statement-id 标识(namespace+id)*/
private String id;

/**
- 参数类型
*/
private String parameterType;

/**
- 返回类型
*/
private String resultType;

/**
- sql 语句
*/
private String sql;

/**
- sql 类型
*/
private SqlType sqlType;

}

XmlConfigBuilder(全局配置解析)

public class XMLConfigBuilder {public Configuration parseConfiguration(InputStream inputStream) throws DocumentException, PropertyVetoException {Configuration configuration = new Configuration();

// 解析数据源配置

Document document = new SAXReader().read(inputStream);

Element rootElement = document.getRootElement();

List<Element> list = rootElement.selectNodes("//datasource/property");

Properties properties = new Properties();

for (Element element : list) {String name = element.attributeValue("name");

String value = element.attributeValue("value");

properties.setProperty(name,value);

}

// 构建连接池

ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();

comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));

comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));

comboPooledDataSource.setUser(properties.getProperty("username"));

comboPooledDataSource.setPassword(properties.getProperty("password"));

configuration.setDataSource(comboPooledDataSource);

// 读取 mapper.xml 文件门路,解析 mapper.xml

List<Element> mapperList = rootElement.selectNodes("//mapper");

for (Element element : mapperList) {String resource = element.attributeValue("resource");

XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);

xmlMapperBuilder.parseMapper(Resources.getResourceAsStream(resource));

}
return configuration;
}
}

XmlMapperStatementBuilder(SQL 语句解析)

public class XMLMapperBuilder {

private Configuration configuration;

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

public void parseMapper(InputStream inputStream) throws DocumentException {

// 解析 SQL statement 并封装到汇合

Document document = new SAXReader().read(inputStream);

Element rootElement = document.getRootElement();

// 解析 sql 语句封装 MapperStatement

buildStatement(rootElement, new String[]{"select", "update", "insert", "delete"});

}

private void parseStatement(String namespace, Element element) {String id = element.attributeValue("id");

String parameterType = element.attributeValue("parameterType");

String resultType = element.attributeValue("resultType");

String sql = element.getTextTrim();

String elementName = element.getName();

SqlType sqlType = SqlType.valueOf(elementName.toUpperCase());

MapperStatement mapperStatement = new MapperStatement();

mapperStatement.setId(id);

mapperStatement.setParameterType(parameterType);

mapperStatement.setResultType(resultType);

mapperStatement.setSql(sql);

mapperStatement.setSqlType(sqlType);

String statementId = namespace + "." + id;

configuration.getMapperStatementMap().put(statementId, mapperStatement);

}

private void buildStatement(Element rootElement, String[] nodes) {String namespace = rootElement.attributeValue("namespace");

for (String node : nodes) {List<Element> list = rootElement.selectNodes("//" + node);

for (Element element : list) {parseStatement(namespace, element);

}

}

}

}

SqlSession 相干接口实现

SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) throws PropertyVetoException, DocumentException {XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();

Configuration configuration = xmlConfigBuilder.parseConfiguration(inputStream);

return new DefaultSqlSessionFactory(configuration);

}

}

DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory{

private final Configuration configuration;

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

@Override

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

}

}

DefaultSqlSession

public class DefaultSqlSession implements SqlSession{public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;}

private Configuration configuration;

@Override

public <E> List<E> selectList(String statementId, Object... params) throws Exception {Executor selectExecutor = new SelectExecutor();

MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);

return selectExecutor.query(configuration, mapperStatement, params);

}

@Override

public <T> T selectOne(String statementId, Object... params) throws Exception {List<Object> objects = selectList(statementId, params);

if (objects.size() == 1) {return (T) objects.get(0);

}else {throw new RuntimeException("查问后果过多或不存在");

}

}

@Override

public int insert(String statementId, Object... params) throws Exception {return this.update(statementId,params);

}

@Override

public int update(String statementId, Object... params) throws Exception {Executor updateExecutor = new UpdateExecutor();

MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);

return updateExecutor.update(configuration,mapperStatement,params);

}

@Override

public int delete(String statementId, Object... params) throws Exception {return this.update(statementId,params);

}

@Override

public <T> T getMapper(Class<?> c) {

Object o = Proxy

.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{c}, new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Class<?> declaringClass = method.getDeclaringClass();

String statementId = declaringClass.getName() + "." + method.getName();

MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);

SqlType sqlType = mapperStatement.getSqlType();

switch (sqlType) {

case SELECT:

Type genericReturnType = method.getGenericReturnType();

// 判断是否进行了 泛型类型参数化

if(genericReturnType instanceof ParameterizedType){return selectList(statementId, args);

}

return selectOne(statementId,args);

case INSERT:

return insert(statementId,args);

case UPDATE:

return update(statementId,args);

case DELETE:

return delete(statementId,args);

default :

break;

}

return null;

}

});

return (T) o;

}

}

参考代码

lov3r/lagou_mybatis

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0