乐趣区

关于java:手撸一个mybatis自定持久框架-实现简单的单表-crud

自定长久框架 mybatis

前言

JDBC 操作数据库和 Mybatis 操作数据库, 为什么应用 Mybatis 框架, 而不去应用原生过的 JDBC 操作数据库呢?
带着这么几个问题, 咱们先来看看原生的 JDBC 操作数据步骤!

JDBC 操作数据库步骤

  • 1 加载驱动
  • 2 创立连贯
  • 3 编译 sql 语句
  • 4 设置参数
  • 5 执行 sql 语句
  • 6 获取后果返回集
  • 7 敞开连贯

JDBC 操作数据库存在的几个痛点:

  • 1 首先第一步加载驱动, 这个咱们齐全能够通过反射来解决, 更换不同的数据库驱动;
  • 2 创立连贯, 每一次操作数据库都要去现成的连贯数据库, 如果操作数据库很频繁, 这种开销很耗费资源, 咱们能够采纳 线程池,` 的思路去解决!
  • 3 3,4 步骤能够一起来看, 编译 sql 语句, 这个通常设置一些参数, 如果是很多参数对象, 常常改变比拟大, 在硬编码过程中, 略微操作不慎可能会代码出错, 改变老本很高, 耦合性很大!
  • 4 执行 sql 没什么可说的;
  • 5 获取后果返回集, 查问而言后果返回, 每个查问承受的后果集不同, 此处, 对象不同, 其余代码都是反复的;
  • 6 敞开连贯

以上几个剖析过程, 简直每次操作都会面临数据库连贯, 敞开, 获取后果集 (返回类型不通过, 对象类型不同) 反复代码很高, 参数设置重大耦合, 改变频繁, 出错率高;

对此反复度高的代码, 能够通过封装利用去解决;
对于驱动, 能够通过配置文件, 更换不同的数据库驱动;
对于频繁连贯, 能够通过连接池去解决;
对于设置参数, 获取后果集, 能够通过配置文件, 以及泛型, 反射, 去封装不同类型的返回后果集;

我的项目构造

### 须要用到的依赖

      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.17</version>
      </dependency>
      <dependency>
          <groupId>c3p0</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.1.2</version>
      </dependency>
      <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.12</version>
      </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.10</version>
      </dependency>
      <dependency>
          <groupId>dom4j</groupId>
          <artifactId>dom4j</artifactId>
          <version>1.6.1</version>
      </dependency>
      <dependency>
          <groupId>jaxen</groupId>
          <artifactId>jaxen</artifactId>
          <version>1.1.6</version>
      </dependency>

自定义框架

客户端

##### 步骤

  • 首先定义数据库配置文件, 配置引入的 sqlMapper 文件

    应用字节输出流将其加载到内存中, 应用过 dom4j 解析封装成 Configuration 对象, 重复使用;

  • 定义 sqlMapper 文件 用来编写 sql 语句, 入参, 出参类型

    加载解析封装对象 MappedStatement 用来保留每个 sqlMapper 每条 sql 语句的入参, 出参类型以及 sql 操作类型;

定义数据库配置文件

首先咱们定义数据库配置文件sqlMapConfigration.xml

<configuration>

    <!-- 数据库连贯信息 -->
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl"  value="jdbc:mysql:///stu_test"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>


    <!--    配置 mapper sql 信息文件 会有多个 -->
    <mapper resource="mapper.xml"></mapper>

</configuration>

定义 Configration

用来保留 数据库配置信息 和 每个 mapper 中 sql 惟一类型 namespace.sql 的 id

public class Configration {

    /**
     * 数据源对象
     */
    private DataSource dataSource;

    /**
     * key 规定是 namesapc + . + id(每个 sql 语句的 id)  设置参数以及返回类型时候应用
     */
    private Map<String,MappedStatement> mapperStamentMap = new HashMap<>();


    public DataSource getDataSource() {return dataSource;}

    public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}

    public Map<String, MappedStatement> getMapperStamentMap() {return mapperStamentMap;}

    public void setMapperStamentMap(Map<String, MappedStatement> mapperStamentMap) {this.mapperStamentMap = mapperStamentMap;}
}

定义 xmlConfigerBuilder 类解析

定义查问语句 sql 配置文件

mapper.xml

定义 sql 以及 sql 入参对象类型,sql 查问返回类型

<mapper namespace="User"> 

<select id="selectOne" paramterType="com.udeam.com.udeam.pojo.User" resultType="com.udeam.com.udeam.pojo.User"> 
select * from user where id = #{id} and name =#{name} 
</select>

<select id="selectList" resultType="com.udeam.com.udeam.pojo.User"> 
select * from user 
</select> 

</mapper>
定义 MappedStatement 实体类

保留每个 mapper 中 sql 的 sql 语句类型 , sql 入参, 返回类型以及 sql 的 id

public class MappedStatement {

    /**
     * sql xml 语句 id 示意每条 sql 的唯一性
     */
    private String id;

    /**
     * sql 入参类型
     */
    private Class<?> paramType;

    /**
     * sql 返回类型
     */
    private Class<?> resultType;

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


    public String getId() {return id;}

    public void setId(String id) {this.id = id;}

    public Class<?> getParamType() {return paramType;}

    public void setParamType(Class<?> paramType) {this.paramType = paramType;}

    public Class<?> getResultType() {return resultType;}

    public void setResultType(Class<?> resultType) {this.resultType = resultType;}

    public String getSql() {return sql;}

    public void setSql(String sql) {this.sql = sql;}
}

定义 SqlSessionFactoryBuilderbuild()办法解析 sqlMapConfigration.xml

public class SqlSessionFactoryBuilder {

    private Configration configration;

    public SqlSessionFactoryBuilder() {this.configration = new Configration();
    }

    public SqlSessionFactory build(InputStream inputStream) throws Exception {
        //1 解析配置文件, 封装 Configuration
        xmlConfigerBuilder xmlConfigerBuilder = new xmlConfigerBuilder(configration);
        //2 解析
        configration = xmlConfigerBuilder.parseConfigration(inputStream);

        // 3 创立 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configration);
        return sqlSessionFactory;
    }

}

定义 xmlConfigerBuilder 类用于

public class xmlConfigerBuilder {

    private Configration configration;


    public xmlConfigerBuilder(Configration  configration) {this.configration = configration;}


    /**
     * 解析封装 xml
     * @return
     */
    public Configration parseConfigration(InputStream inputStream) throws Exception {Document read = new SAXReader().read(inputStream);

        // 获取跟标签
        Element rootElement = read.getRootElement();
        //1 设置 datasource 属性
        List<Element> elementList = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (int i = 0; i < elementList.size(); i++) {
            // 设置属性值
            String name = elementList.get(i).attributeValue("name");
            String value = elementList.get(i).attributeValue("value");
            properties.setProperty(name,value);
        }
        // 设置连接池属性 , 这里应用 c3p0 连接池
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setUser(properties.getProperty("username"));
        comboPooledDataSource.setPassword(properties.getProperty("password"));
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        // 设置 datasource
        configration.setDataSource(comboPooledDataSource);

        //2 封装解析 mapper 属性
        // 读取 mapper 设置 mapper 返回类型 以及 sql 等封装 MappedStatement 对象
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configration);
        List<Element> list = rootElement.selectNodes("//mapper");
        for (Element element : list) {String resource = element.attributeValue("resource");
            InputStream inputStream1 = Resource.inputStream(resource);
            // 读取每一个 mapper xml
            xmlMapperBuilder.parse(inputStream1);
        }
        return configration;
    }
}

定义 XMLMapperBuilder 类解析 Mapper 类中信息, 解析 Mapper 封装配置类中每个 sql 的 sql 语句以及返回类型, 入参类型

解析 mapper 封装 sql 语句属性到 MappedStatement

public class XMLMapperBuilder {

    private Configration configration;

    public XMLMapperBuilder(Configration configration) {this.configration = configration;}

    public void parse(InputStream inputStream1) throws DocumentException, ClassNotFoundException {Document read = new SAXReader().read(inputStream1);
        Element rootElement = read.getRootElement();

        // 获取 namespace
        String namespace = rootElement.attributeValue("namespace");
        // 读取每一个查问标签
        List<Element> list = rootElement.selectNodes("//select");
        for (Element element : list) {MappedStatement mappedStatement = new MappedStatement();
            // 获取 sql
            String sql = element.getTextTrim();
            mappedStatement.setSql(sql);
            // 设置 id
            String id = element.attributeValue("id");
            mappedStatement.setId(id);

            // 设置入参类型
            String paramterType = element.attributeValue("paramterType");
            mappedStatement.setParamType(getClassType(paramterType));

            // 设置返回类型
            String resultType = element.attributeValue("resultType");
            mappedStatement.setResultType(getClassType(resultType));

            // 设置 mapperStamentMap
            configration.getMapperStamentMap().put(namespace + "." + id, mappedStatement);
        }
    }


    public Class<?> getClassType(String type) throws ClassNotFoundException {if(type==null){return null;}

        Class<?> clasz = Class.forName(type);
        return clasz;

    }
}

定义 Resource 类读取 xml

public class Resource {

    /**
     * 加载配置文件工具类
     * @param name
     * @return
     * @throws FileNotFoundException
     */
    public static InputStream inputStream(String name) throws Exception {
        // 应用类加载器加载配置文件
        InputStream inputStream = Resource.class.getClassLoader().getResourceAsStream(name);
        return inputStream;
    }
}

查问

定义查问接口 SqlSession
public interface SqlSession {

    // 查问多个
    public <E>List<E> selectList(String statementId,Object...params) throws IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException;

    // 查问单个
    public <T> T  selectOne(String statementId,Object...params) throws IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException;


    public void close() throws Exception;}

sql 语句执行器 Excutor 接口

public interface Excutor {

    /**
     * 查问接口
     * @param configration 数据库配置类
     * @param mappedStatement mapper 信息对象
     * @param params 参数
     * @param <E>
     * @return
     */
    <E> List<E> query(Configration configration, MappedStatement mappedStatement,Object[] params) throws SQLException, IllegalAccessException, InstantiationException, NoSuchFieldException, IntrospectionException, InvocationTargetException;

    void close() throws Exception;}

sql 语句执行器实现类
具体的 sql 执行器 (mybatis 中有三个), 默认的是 SimpleExcutor

这里面对通过传入的配置文件以及具体的 key(namespace.sql id)从 MappedStatement 获取 sql 以及 sql 入参返回类型
而后通过反射区设置参数, 获取后果返回集;

其中 BoundSql类是对 xml 中 sql 进行解决, 将其转换为 ? 占位符, 解析出 #{}外面的值进行存储, 而后再去执行后续的赋值操作!

public class SimpleExcutor implements Excutor {

    private Connection connection;

    @Override
    public <E> List<E> query(Configration configration, MappedStatement mappedStatement, Object[] params) throws SQLException, IllegalAccessException, InstantiationException, NoSuchFieldException, IntrospectionException, InvocationTargetException {

        //1 获取连贯
        connection = configration.getDataSource().getConnection();

        //2 获取 sql select * from user where id = #{id} and name = #{name}
        String sql = mappedStatement.getSql();
        // 对 sql 进行解决    // 转换 sql 语句:select * from user where id = ? and name = ?,转换的过程中,还须要对 #{}外面的值进行解析存储
        BoundSql boundSql = getBoundSql(sql);

        // 最终的 sql
        String finalSql = boundSql.getSqlText();

        // 3 预编译对象
        PreparedStatement preparedStatement = connection.prepareStatement(finalSql);


        // 获取传入的参数类型
        Class<?> paramType = mappedStatement.getParamType();


        // 4 获取传入参数
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();

        // 设置参数
        for (int i = 0; i < parameterMappingList.size(); i++) {String content = parameterMappingList.get(i).getContent();
            // 反射设置值
            Field declaredField = paramType.getDeclaredField(content);
            // 强制拜访
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);
            // 占位符设置值  列是从 1 开始的
            preparedStatement.setObject(i + 1, o);
            System.out.println("以后属性是" + content + "值是 :" + o);
        }

        // 5. 执行 sql
        ResultSet resultSet = preparedStatement.executeQuery();
        // 返回的参数类型
        Class<?> resultType = mappedStatement.getResultType();

        ArrayList<E> objects = new ArrayList<>();
        while (resultSet.next()) {
            // 创建对象
            Object o = (E) 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);
                // 应用内省技术 也能够应用反射技术
                // 创立属性形容器,为属性生成读写办法
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
                // 获取写办法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                // 向类中写入值
                writeMethod.invoke(o, value);
            }

            objects.add((E) o);
        }


        return objects;
    }


    @Override
    public void close() throws Exception {connection.close();
    }



    /**
     * 实现对 #{}的解析工作:1. 将#{}应用?进行代替,2. 解析出#{}外面的值进行存储
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        // 标记解决类:配置标记解析器来实现对占位符的解析解决工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // 解析进去的 sql
        String parseSql = genericTokenParser.parse(sql);
        //#{}外面解析进去的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parseSql,parameterMappings);
        return boundSql;

    }
}

具体的实现查问接口如 DefaultSqlSession
默认的 sqlsession 实现类(mybatis 中默认的 DefaultSqlSession)

public class DefaultSqlSession implements SqlSession {


    private Configration configration;

    //sql 执行器
    private Excutor simpleExcutor = new SimpleExcutor();

    public DefaultSqlSession(Configration configration) {this.configration = configration;}

    @Override
    public <E> List<E> selectList(String statementId, Object... params) throws IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException {

        // 依据 statementId 获取 MappedStatement 对象
        MappedStatement mappedStatement = configration.getMapperStamentMap().get(statementId);

        //sql 执行器
        List<Object> query = simpleExcutor.query(configration, mappedStatement, params);
        return (List<E>) query;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) throws IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException {List<Object> objects = selectList(statementId, params);
        if (objects==null || objects.size() == 0){return null;}
        if (objects.size()>1){throw new RuntimeException("存在多个值!");
        }

        return (T) objects.get(0);
    }

    @Override
    public void close() throws Exception {simpleExcutor.close();
    }

}

定义 SqlSessionFactory 工厂用来生产不同的 sqlSession 去执行 sql
获取 SqlSession 示例 以及对象接口

public interface SqlSessionFactory {public SqlSession openSqlSession();
}

具体工厂实现类, 生产 sqlsession 对象执行增删改查操作

public class DefaultSqlSessionFactory  implements SqlSessionFactory {


    private Configration configration;

    public DefaultSqlSessionFactory(Configration configration) {this.configration = configration;}

    @Override
    public SqlSession openSqlSession() {return new DefaultSqlSession(configration);
    }

解析的 #{id}成站位符 ? 工具类, 以及内省创建对象工具类
GenericTokenParser

public class GenericTokenParser {

  private final String openToken; // 开始标记
  private final String closeToken; // 完结标记
  private final TokenHandler handler; // 标记处理器

  public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
    this.openToken = openToken;
    this.closeToken = closeToken;
    this.handler = handler;
  }

  /**
   * 解析 ${}和 #{}
   * @param text
   * @return
   * 该办法次要实现了配置文件、脚本等片段中占位符的解析、解决工作,并返回最终须要的数据。* 其中,解析工作由该办法实现,解决工作是由处理器 handler 的 handleToken()办法来实现
   */
  public String parse(String text) {
    // 验证参数问题,如果是 null,就返回空字符串。if (text == null || text.isEmpty()) {return "";}

    // 上面持续验证是否蕴含开始标签,如果不蕴含,默认不是占位符,间接原样返回即可,否则继续执行。int start = text.indexOf(openToken, 0);
    if (start == -1) {return text;}

   // 把 text 转成字符数组 src,并且定义默认偏移量 offset=0、存储最终须要返回字符串的变量 builder,// text 变量中占位符对应的变量名 expression。判断 start 是否大于 -1(即 text 中是否存在 openToken),如果存在就执行上面代码
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    while (start > -1) {
     // 判断如果开始标记前如果有转义字符,就不作为 openToken 进行解决,否则持续解决
      if (start > 0 && src[start - 1] == '\\') {builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();} else {
        // 重置 expression 变量,防止空指针或者老数据烦扰。if (expression == null) {expression = new StringBuilder();
        } else {expression.setLength(0);
        }
        builder.append(src, offset, start - offset);
        offset = start + openToken.length();
        int end = text.indexOf(closeToken, offset);
        while (end > -1) {//// 存在完结标记时
          if (end > offset && src[end - 1] == '\\') {// 如果完结标记后面有转义字符时
            // this close token is escaped. remove the backslash and continue.
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else {// 不存在转义字符,即须要作为参数进行解决
            expression.append(src, offset, end - offset);
            offset = end + closeToken.length();
            break;
          }
        }
        if (end == -1) {
          // close token was not found.
          builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
          // 首先依据参数的 key(即 expression)进行参数解决,返回? 作为占位符
          builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();}
      }
      start = text.indexOf(openToken, offset);
    }
    if (offset < src.length) {builder.append(src, offset, src.length - offset);
    }
    return builder.toString();}
}

ParameterMapping


public class ParameterMapping {

    private String content;

    public ParameterMapping(String content) {this.content = content;}

    public String getContent() {return content;}

    public void setContent(String content) {this.content = content;}
}

ParameterMappingTokenHandler

public class ParameterMappingTokenHandler implements TokenHandler {private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();

    // context 是参数名称 #{id} #{username}

    public String handleToken(String content) {parameterMappings.add(buildParameterMapping(content));
        return "?";
    }

    private ParameterMapping buildParameterMapping(String content) {ParameterMapping parameterMapping = new ParameterMapping(content);
        return parameterMapping;
    }

    public List<ParameterMapping> getParameterMappings() {return parameterMappings;}

    public void setParameterMappings(List<ParameterMapping> parameterMappings) {this.parameterMappings = parameterMappings;}

}

TokenHandler

public interface TokenHandler {String handleToken(String content);
}
一般测试
    String name = "sqlMapConfigration.xml";
    // 1 加载 xml 配置文件
    InputStream inputStream = Resource.inputStream(name);
    // 2 解析配置文件
       // 初始化 Configration 初始化容器 mapperStamentMap 容器保留 mapper 中 sql 信息
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    // 3 创立会话
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSqlSession();
    //4 查问
    User user = new User();
    user.setId(2);
    user.setName("小王");
    
    // 非代理模式
    User user1 = sqlSession.selectOne("User.selectOne", user);
    System.out.println(user1);
    List<User> usersList = sqlSession.selectList("User.selectList");
    System.out.println(usersList); 

这是应用 namespace.id 形式硬编码去查问, 但理论过程中咱们间接通过 service 层调用 dao 层 mapper 去查问执行的;

故此, 咱们须要想 mybatis 那样定义一个 mapper 接口类, 而后应用动静代理调用执行;

在 SqlSession 接口中定义一个 mapper 代理接口

    /**
     * Mapper 代理接口
     * @param mapperClass
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<?> mapperClass);
UserMapper 创立

public interface UserMapper {

    /**
     * 查问所有
     * @return
     */
    List<User> selectList2();

    /**
     * 查问单个 依据条件
     * @param user
     * @return
     */
    User selectOne2(User user);
}

新创建 mapper2.xml


<!--mapper 代理模式
 语句 id 必须和 mapper 中查问语句办法名保持一致
 namespace 必须是类的权限定命名

 起因是 JDK 动静代理中 无奈提供对应的 namespace 和查问语句配置 id
 故此用办法名和 mapper 类的全限定命名进行应用 key 从获取 mapper 配置文件 sql 语句的入参, 返回类型;
-->

<mapper namespace="com.udeam.test.mapper.UserMapper">

    <!--    示意查问单个 -->
    <select id="selectOne2" paramterType="com.udeam.pojo.User" resultType="com.udeam.pojo.User">
        select * from user where id = #{id} and name = #{name}
    </select>


    <!--    示意查问多个 -->
    <select id="selectList2" resultType="com.udeam.pojo.User">
        select * from user
    </select>

</mapper>

而后在 SqlMapXml 里增加进去 mapper

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

在子类中去实现这个代理办法

这里须要留神的是

  • 在上面 jdk 代理外面, 咱们无奈拿到 xml 文件里 namespace 和 sql id
  • 实际上应用 mybatis 时候,mapper 代理的办法名 , 和mapper,xml 里的会 保持一致,namspacs 会应用该 mapper 的权限定名;

在 JDK 动静代理中应用办法名和全门路去从 Configration 封装的 map 对象去获取 xml 里的 sql 的入参类型和返回类型;

    /**
     * 应用 JDK 动静代理来执行 mapper
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {Object o = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 获取 class 的权限定命名
                String className = method.getDeclaringClass().getName();
                // 获取办法名
                String name = method.getName();
                // 拼接 statementid 从 map 中获取 sql 入参类型, 返回类型
                String statementid = className + "." + name;
                // 判断是否实现泛型类型参数化
                Type genericReturnType = method.getGenericReturnType();
                if (genericReturnType instanceof ParameterizedType) {
                    // 还是去执行查询方法
                    return selectList(statementid, args);
                }
                return selectOne(statementid, args);


            }
        });

       return (T) o;
    }
mapper 代理测试
  // 代理测试
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(userMapper.selectList2());

以上仅仅是实现了单表的查问操作, 和传入的固定参数, 对动静 sql 和删除在 底部 源代码中实现了, 能够 下载 下来康康, 删除和新增根本实现形式一样;

用到的设计模式

  • 工厂模式
    在创立不同的 sqlSession 时进行应用, 能够抉择 new 不同的子类;
  • 代理模式
    应用 JDK 动静代理对 Mapper 进行代理
  • 构建者模式
    在 SqlSessionFactoryBuilder 类中 build()办法中通过对 Configration 对象的属性构建;

代码地址

点击下载

退出移动版