当前位置: 首页 > 图灵资讯 > 技术篇> 手写mybatis框架1

手写mybatis框架1

来源:图灵教育
时间:2023-06-02 09:26:18

引⼊相关依赖35pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.god.ibatis</groupId>    <artifactId>course7</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>jar</packaging>    <!--依赖-->    <dependencies>        <!--依赖dom4j-->        <dependency>            <groupId>org.dom4j</groupId>            <artifactId>dom4j</artifactId>            <version>2.1.3</version>        </dependency>        <!--依赖jaxen-->        <dependency>            <groupId>jaxen</groupId>            <artifactId>jaxen</artifactId>            <version>1.2.0</version>        </dependency>        <!--依赖junit-->        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.13.2</version>            <scope>test</scope>        </dependency>        <!--mysql驱动依赖-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>8.0.30</version>        </dependency>    </dependencies>    <properties>        <!--    jdk版本用于编译代码-->        <maven.compiler.source>1.8</maven.compiler.source>        <!--    使用jdk版本的操作程序-->        <maven.compiler.target>1.8</maven.compiler.target>    </properties></project>

1.创建基本类35代码的第一步是org.god.ibatis.工具类utilsresources专门完成“类路径”中资源的加载。

package org.god.ibatis.utils;import java.io.InputStream;/** * godbatis框架提供的工具类。   35 * 该工具专门完成了“类路径”中资源的加载。 */public class Resources {    /**     * 建议将工具的结构方法私有化。     * 因为工具中的方法是静态的,不需要创建对象就可以调用。     * 所有结构方法都是私有化的,以避免new对象。     * 这只是一种编程习惯。     */    private Resources(){}    /**     * 从类路径中加载资源。     * @param resource 资源文件放置在类路径中。     * @return 指向资源文件的输入流。     */    public static InputStream getResourceAsStream(String resource){        return ClassLoader.getSystemClassLoader().getResourceAsStream(resource);    }}

2.第二步分析SqlSessionFactory应有的属性362.1MappedStatement36

普通java类。POJO,SQL标签封装。36

MappedStatement对象对应SQL标签。

SQL标签中的所有信息都包装在MapedStatement对象中。

手写mybatis框架1_mybatis

代码在org.god.ibatis.coreMappedStatement

package org.god.ibatis.core;/** * 普通java类。POJO,SQL标签封装。   36 * MappedStatement对象对应SQL标签。 * SQL标签中的所有信息都包装在MapedStatement对象中。 * 面向对象的编程思想。 */public class MappedStatement {    /**     * sql语句     */    private String sql;    /**     * 要封装的结果集类型。有时候resulttype是null。     * 比如:insert delete 在update语句中,resulttype是null。     * 只有当sql语句是select语句时,resulttype才值得。     */    private String resultType;    @Override    public String toString() {        return "MappedStatement{" +                "sql='" + sql + '\'' +                ", resultType='" + resultType + '\'' +                '}';    }    public String getSql() {        return sql;    }    public void setSql(String sql) {        this.sql = sql;    }    public String getResultType() {        return resultType;    }    public void setResultType(String resultType) {        this.resultType = resultType;    }    public MappedStatement(String sql, String resultType) {        this.sql = sql;        this.resultType = resultType;    }    public MappedStatement() {    }}

2.1分析SqlSessionFactoryBuilder类35-3636

提供⼀个⽆参数构造⽅法,再提供⼀build⽅法,build⽅返回SqlSessionFactory对象

package org.god.ibatis.core;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.Node;import org.dom4j.io.SAXReader;import org.god.ibatis.utils.Resources;import javax.sql.DataSource;import java.io.InputStream;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * SqlSessionFactory构建器对象。 * 通过SqlSessionFactorybuilderbuild方法进行分析 * godbatis-config.xml文件,然后创建SqlSessionFactory对象。 */public class SqlSessionFactoryBuilder {    /**     * 无参数结构方法。     */    public SqlSessionFactoryBuilder(){}    /**     * godbatis分析-config.构建SqlSessionFactory对象的xml文件。     * @param in 指向godbatis指向godbatis-config.xml文件的输入流。     * @param in 指向godbatis指向godbatis-config.xml文件的输入流。     * @return SqlSessionFactory对象。     */    public SqlSessionFactory build(InputStream in){        SqlSessionFactory factory = null;        try {            // godbatis分析-config.xml文件            SAXReader reader = new SAXReader();            Document document = reader.read(in);            Element environments = (Element) document.selectSingleNode("/configuration/environments");            String defaultId = environments.attributeValue("default");            Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");            Element transactionElt = environment.element("transactionManager");            Element dataSourceElt = environment.element("dataSource");            List<String> sqlMapperXMLPathList = new ArrayList<>();            // 在整个配置文件中获取所有mapper标签            List<Node> nodes = document.selectNodes("//mapper");            nodes.forEach(node -> {                Element mapper = (Element) node;                String resource = mapper.attributeValue("resource");                sqlMapperXMLPathList.add(resource);            });            // 获取数据源对象            DataSource dataSource = getDataSource(dataSourceElt);            // 获取事务管理器            Transaction transaction = getTransaction(transactionElt,dataSource);            // mappedstatements获取mapped            Map<String, MappedStatement> mappedStatements = getMappedStatements(sqlMapperXMLPathList);            // 分析完成后,构建SqlSessionFactory对象。            factory = new SqlSessionFactory(transaction, mappedStatements);        } catch (Exception e) {            e.printStackTrace();        }        return factory;    }    /**     * 所有Sqlmaper分析.xml文件,然后构建Map集合。        43     * @param sqlMapperXMLPathList     * @return     */    private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXMLPathList) {        Map<String, MappedStatement> mappedStatements = new HashMap<>();        sqlMapperXMLPathList.forEach(sqlMapperXMLPath -> {            try {                SAXReader reader = new SAXReader();                Document document = reader.read(Resources.getResourceAsStream(sqlMapperXMLPath));                Element mapper = (Element) document.selectSingleNode("mapper");                String namespace = mapper.attributeValue("namespace");                List<Element> elements = mapper.elements();                elements.forEach(element -> {                    String id = element.attributeValue("id");                    // namespace和id拼接在这里,生成最终的sqlid                    String sqlId = namespace + "." + id;                    String resultType = element.attributeValue("resultType");                    String sql = element.getTextTrim();                    MappedStatement mappedStatement = new MappedStatement(sql, resultType);                    mappedStatements.put(sqlId, mappedStatement);                });            } catch (Exception e) {                e.printStackTrace();            }        });        return mappedStatements;    }    /**     * 获取事务管理器    42     * @param transactionElt 事务管理器的标签元素     * @param dataSource 数据源对象     * @return     */    private Transaction getTransaction(Element transactionElt, DataSource dataSource) {        Transaction transaction = null;        String type = transactionElt.attributeValue("type").trim().toUpperCase();        if (Const.JDBC_TRANSACTION.equals(type)) {            transaction = new JdbcTransaction(dataSource, false); // 默认情况下,事务将开始,将来需要手动提交。        }        if (Const.MANAGED_TRANSACTION.equals(type)) {            transaction = new ManagedTransaction();        }        return transaction;    }    /**     * 获取数据源对象     41     * @param dataSourceElt 数据源标签元素     * @return     */    private DataSource getDataSource(Element dataSourceElt) {        Map<String,String> map = new HashMap<>();        // 获得所有property        List<Element> propertyElts = dataSourceElt.elements("property");        propertyElts.forEach(propertyElt -> {            String name = propertyElt.attributeValue("name");            String value = propertyElt.attributeValue("value");            map.put(name, value);        });        DataSource dataSource = null;        String type = dataSourceElt.attributeValue("type").trim().toUpperCase();        if (Const.UN_POOLED_DATASOURCE.equals(type)) {            dataSource = new UnPooledDataSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password"));        }        if (Const.POOLED_DATASOURCE.equals(type)) {            dataSource = new PooledDataSource();        }        if (Const.JNDI_DATASOURCE.equals(type)) {            dataSource = new JNDIDataSource();        }        return dataSource;    }}

2.2分析SqlSessionFactory36-37代码.god.ibatis.core37

SqlSessionFactory

package org.god.ibatis.core;import java.util.Map;/** * SqlSessionFactory对象:     35 *      SqlSessionFactory对应一个数据库。 *      SqlSesssionFactory对象可以通过Sqlsesion获得。(开会) *      SqlSessionFactory对象可以打开多个SqlSession会话。 */public class SqlSessionFactory {    /**     * 事务管理器属性      37     * 可灵活切换事务管理器。     * SqlSessionFactory类中的事务管理器应面向接口编程。     * SqlSessionFactory类应该有一个事务管理器接口。     */    private Transaction transaction;    /**     * Map集合存储sql语句。    36     * key是sqlId     * value是相应的SQL标签信息对象。     */    private Map mappedStatements;    public Transaction getTransaction() {        return transaction;    }    public void setTransaction(Transaction transaction) {        this.transaction = transaction;    }    public Map getMappedStatements() {        return mappedStatements;    }    public void setMappedStatements(Map mappedStatements) {        this.mappedStatements = mappedStatements;    }    /**     * 获取SQL会话对象。  45     * @return     */    public SqlSession openSession(){        // 打开会话的前提是打开连接。(连接打开)        transaction.openConnection();        // 创建SqlSession对象        SqlSession sqlSession = new SqlSession(this);        return sqlSession;    }    public SqlSessionFactory(Transaction transaction, Map mappedStatements) {        this.transaction = transaction;        this.mappedStatements = mappedStatements;    }    public SqlSessionFactory() {    }}

3.第三步提取事务管理器接口373.1transation事务管理器接口。

事务管理器接口。

所有事务管理器都应遵循这一规范。

这个界面应该由JDBC事务管理器和MANAGED事务管理器实现。

Transaction事务管理器:提供管理事务的方法。

代码在org.god.ibatis.core37,40transaction

package org.god.ibatis.core;import java.sql.Connection;/** * 事务管理器接口。    37 * 所有事务管理器都应遵循这一规范。 * 这个界面应该由JDBC事务管理器和MANAGED事务管理器实现。 * Transaction事务管理器:提供管理事务的方法。 */public interface Transaction {    /**     * 提交事务     */    void commit();    /**     * 回滚事务     */    void rollback();    /**     * 关闭事务     */    void close();    /**     * 真正打开数据库连接。     */    void openConnection();    /**     * 获取数据库连接对象。     */    void openConnection();    /**     * 获取数据库连接对象。     */    Connection getConnection();}

4.事务管理器实现384.1分析Jdbctransaction实现transation接口38代码.god.ibatis.coreJdbctransaction.2Managedtransaction实现transation接口38代码.god.ibatis.coreManagedTransaction

package org.god.ibatis.core;import java.sql.Connection;/** * MANAGED事务管理器。(godbatis不再实现这一类。)   38 */public class ManagedTransaction implements Transaction{    @Override    public void commit() {    }    @Override    public void rollback() {    }    @Override    public void close() {    }    @Override    public void openConnection() {    }    @Override    public Connection getConnection() {        return null;    }}

5.第五步数据源实现395.1UnPoleddataSource实现类39代码.god.ibatis.coreUnPooledDataSource

package org.god.ibatis.core;import java.io.PrintWriter;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * 实现数据源:UNPOOLED (重点实现这种方式。)    38 * Conection对象每次都不使用连接池。 */public class UnPooledDataSource implements javax.sql.DataSource{    private String url;    private String username;    private String password;    /**     * 创建数据源对象。     * @param driver     * @param url     * @param username     * @param password     */    public UnPooledDataSource(String driver, String url, String username, String password) {        try {            // 直接注册驱动            Class.forName(driver);        } catch (ClassNotFoundException e) {            throw new RuntimeException(e);        }        this.url = url;        this.username = username;        this.password = password;    }    @Override    public Connection getConnection() throws SQLException {        Connection connection = DriverManager.getConnection(url, username, password);        return connection;    }    @Override    public Connection getConnection(String username, String password) throws SQLException {        return null;    }    @Override    public PrintWriter getLogWriter() throws SQLException {        return null;    }    @Override    public void setLogWriter(PrintWriter out) throws SQLException {    }    @Override    public void setLoginTimeout(int seconds) throws SQLException {    }    @Override    public int getLoginTimeout() throws SQLException {        return 0;    }    @Override    public Logger getParentLogger() throws SQLFeatureNotSupportedException {        return null;    }    @Override    public  T unwrap(Class iface) throws SQLException {        return null;    }    @Override    public boolean isWrapperFor(Class iface) throws SQLException {        return false;    }}

5.2PoleddataSource实现类39代码.god.ibatis.corePooledDataSource

package org.god.ibatis.core;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * 实现数据源:POOLED       38 * Conection对象采用godbatis框架内置的数据库连接池获取。(这一点没有实现。) */public class PooledDataSource implements javax.sql.DataSource{    @Override    public Connection getConnection() throws SQLException {        // Conection对象从数据库连接池中获取。(这个数据库连接池包装在我的godbatis框架内。)        return null;    }    @Override    public Connection getConnection(String username, String password) throws SQLException {        return null;    }    @Override    public PrintWriter getLogWriter() throws SQLException {        return null;    }    @Override    public void setLogWriter(PrintWriter out) throws SQLException {    }    @Override    public void setLoginTimeout(int seconds) throws SQLException {    }    @Override    public int getLoginTimeout() throws SQLException {        return 0;    }    @Override    public Logger getParentLogger() throws SQLFeatureNotSupportedException {        return null;    }    @Override    public  T unwrap(Class iface) throws SQLException {        return null;    }    @Override    public boolean isWrapperFor(Class iface) throws SQLException {        return false;    }}

5.3JNDIDataSource实现类39代码.god.ibatis.core JNDIDataSource

package org.god.ibatis.core;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;/** * 实现数据源:JNDI      38 * 使用第三方数据库连接池获取Conection对象。(这一点没有实现。) */public class JNDIDataSource implements javax.sql.DataSource{    @Override    public Connection getConnection() throws SQLException {        return null;    }    @Override    public Connection getConnection(String username, String password) throws SQLException {        return null;    }    @Override    public PrintWriter getLogWriter() throws SQLException {        return null;    }    @Override    public void setLogWriter(PrintWriter out) throws SQLException {    }    @Override    public void setLoginTimeout(int seconds) throws SQLException {    }    @Override    public int getLoginTimeout() throws SQLException {        return 0;    }    @Override    public Logger getParentLogger() throws SQLFeatureNotSupportedException {        return null;    }    @Override    public  T unwrap(Class iface) throws SQLException {        return null;    }    @Override    public boolean isWrapperFor(Class iface) throws SQLException {        return false;    }}

6.事务管理器改造第六步400步代码在org.god.ibatis.core40JdbcTransaction

package org.god.ibatis.core;import javax.sql.DataSource;import java.sql.Connection;import java.sql.SQLException;/** * JDBC事务管理器(godbatis框架目前只实现JDBCtransaction。)    38 */public class JdbcTransaction implements Transaction{    /**     * 数据源属性     * 经典设计:面向接口编程。     */    private DataSource dataSource;    /**     * 自动提交标志     * true表示自动提交     * false表示不使用自动提交     */    private boolean autoCommit;    /**     * 连接对象     */    private Connection connection;    @Override    public Connection getConnection() {        return connection;    }    /**     * 创建事务管理器对象     * @param dataSource     * @param autoCommit     */    public JdbcTransaction(DataSource dataSource, boolean autoCommit) {        this.dataSource = dataSource;        this.autoCommit = autoCommit;    }    @Override    public void commit() {        try {            connection.commit();        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public void rollback() {        try {            connection.rollback();        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public void close() {        try {            connection.close();        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public void openConnection(){        if (connection == null) {            try {                connection = dataSource.getConnection();                // 开启事务                connection.setAutoCommit(autoCommit);            } catch (SQLException e) {                e.printStackTrace();            }        }    }}

7.第七步获取数据源对象41代码.god.ibatis.core41是SqlsessionFactorybuilder中的方法getdatasourcer

/**     * 获取数据源对象     * @param dataSourceElt 数据源标签元素     * @return     */    private DataSource getDataSource(Element dataSourceElt) {        Map map = new HashMap<>();        // 获得所有property        List propertyElts = dataSourceElt.elements("property");        propertyElts.forEach(propertyElt -> {            String name = propertyElt.attributeValue("name");            String value = propertyElt.attributeValue("value");            map.put(name, value);        });        DataSource dataSource = null;        String type = dataSourceElt.attributeValue("type").trim().toUpperCase();        if (Const.UN_POOLED_DATASOURCE.equals(type)) {            dataSource = new UnPooledDataSource(map.get("driver"), map.get("url"), map.get("username"), map.get("password"));        }        if (Const.POOLED_DATASOURCE.equals(type)) {            dataSource = new PooledDataSource();        }        if (Const.JNDI_DATASOURCE.equals(type)) {            dataSource = new JNDIDataSource();        }        return dataSource;    }

8.第八步是获取事务管理器对象的42代码.god.ibatis.core42是SqlsessionFactorybuilder中的方法gettransaction

**     * 获取事务管理器     * @param transactionElt 事务管理器的标签元素     * @param dataSource 数据源对象     * @return     */    private Transaction getTransaction(Element transactionElt, DataSource dataSource) {        Transaction transaction = null;        String type = transactionElt.attributeValue("type").trim().toUpperCase();        if (Const.JDBC_TRANSACTION.equals(type)) {            transaction = new JdbcTransaction(dataSource, false); // 默认情况下,事务将开始,将来需要手动提交。        }        if (Const.MANAGED_TRANSACTION.equals(type)) {            transaction = new ManagedTransaction();        }        return transaction;    }

9.SQLMap在org中集合43代码,第九步获取存储.god.ibatis.core43是SqlsessionFactorybuilder中的方法。

/**     * 所有Sqlmaper分析.然后构建Map集合的xml文件。     * @param sqlMapperXMLPathList     * @return     */    private Map getMappedStatements(List sqlMapperXMLPathList) {        Map mappedStatements = new HashMap<>();        sqlMapperXMLPathList.forEach(sqlMapperXMLPath -> {            try {                SAXReader reader = new SAXReader();                Document document = reader.read(Resources.getResourceAsStream(sqlMapperXMLPath));                Element mapper = (Element) document.selectSingleNode("mapper");                String namespace = mapper.attributeValue("namespace");                List elements = mapper.elements();                elements.forEach(element -> {                    String id = element.attributeValue("id");                    // namespace和id拼接在这里,生成最终的sqlid                    String sqlId = namespace + "." + id;                    String resultType = element.attributeValue("resultType");                    String sql = element.getTextTrim();                    MappedStatement mappedStatement = new MappedStatement(sql, resultType);                    mappedStatements.put(sqlId, mappedStatement);                });            } catch (Exception e) {                e.printStackTrace();            }        });        return mappedStatements;    }

10.第十步测试框架44代码org.god.ibatis.test44是GodBatistestest中的方法testsssionFactory

@Test    public void testSqlSessionFactory(){        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("godbatis-config.xml"));        System.out.println(sqlSessionFactory);    }

11.第十一十二十三步45-47包装SqlSession对象45代码.god.ibatis.core46-47Sqlsession

package org.god.ibatis.core;import java.lang.reflect.Method;import java.sql.*;/** * SQL语句的会话对象专门负责SQL语句的执行     45 */public class SqlSession {    private SqlSessionFactory factory;    public SqlSession(SqlSessionFactory factory) {        this.factory = factory;    }    /**     * 执行insert语句,将记录插入数据库表中。  46     * @param sqlId sql语句的id     * @param pojo 插入的数据。     * @return     */    public int insert(String sqlId, Object pojo){        int count = 0;        try {            // JDBC代码,执行insert语句,完成插入操作。            Connection connection = factory.getTransaction().getConnection();            // insert into t_user values(#{id},#{name},#{age})            String godbatisSql = factory.getMappedStatements().get(sqlId).getSql();            // insert into t_user(id,name,age) values(?,?,?)            String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?");            PreparedStatement ps = connection.prepareStatement(sql);            // 给?占位符传值    47            // 难度是什么:            // 第一:你不知道有多少人?            // 第二:你不知道在pojo对象中赋予哪个属性 ?            // ps.String(几个问号, 传什么值); // 这里都是setstring,所以数据库表中的字段类型要求都是varchar。这是godbatis失败的地方。            int fromIndex = 0;            int index = 1;            while(true){                int jingIndex = godbatisSql.indexOf("#", fromIndex);                if (jingIndex < 0) {                    break;                }                int youKuoHaoIndex = godbatisSql.indexOf("}", fromIndex);                String propertyName = godbatisSql.substring(jingIndex + 2, youKuoHaoIndex).trim();                fromIndex = youKuoHaoIndex + 1;                // 有属性名id,如何获得id的属性值?调用getid()方法                //拼方法名                String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);                ///使用反射机制根据方法名获得方法对象                Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName);                ///利用反射机制根据调用获得的方法获得属性值                Object propertyValue = getMethod.invoke(pojo);                //给?赋值                ps.setString(index, propertyValue.toString());                index++;            }            // 执行SQL语句            count = ps.executeUpdate();        } catch (Exception e) {            e.printStackTrace();        }        return count;    }    /**     * 执行查询语句,返回一个对象。该方法仅适用于返回记录的sql语句。   49     * @param sqlId     * @param param     * @return     */    public Object selectOne(String sqlId, Object param){        Object obj = null;        try {            Connection connection = factory.getTransaction().getConnection();            MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId);            // 这是DQL查询的句子            // select * from t_user where id = #{id}            String godbatisSql = mappedStatement.getSql();            String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?");            PreparedStatement ps = connection.prepareStatement(sql);            // 给占位符传值            ps.setString(1, param.toString());            // 查询返回结果集            ResultSet rs = ps.executeQuery();            // 包装的结果类型。            String resultType = mappedStatement.getResultType(); // org.god.ibatis.pojo.User            // 从结果中集中取数据,java对象的封装            if (rs.next()) {                //使用反射                // Classss获取resulttype                Class resultTypeClass = Class.forName(resultType);                // 调用无参数构建方法创建对象                obj = resultTypeClass.newInstance(); // Object obj = new User();                // 给User类id,name,age属性赋值                // 赋予obj对象哪个属性哪个值。    50                /*                mysql> select * from t_user where id = '1111';                +------+----------+------+                | id   | name     | age  |                +------+----------+------+                | 1111 | zhangsan | 20   |                +------+----------+------+                解决问题的关键:将查询结果的列名作为属性名。                列名是id,所以属性名是:id                排名是name,所以属性名是:name                 */                ResultSetMetaData rsmd = rs.getMetaData();                int columnCount = rsmd.getColumnCount();                for (int i = 0; i < columnCount; i++) {                    //getcolumnname                    String propertyName = rsmd.getColumnName(i + 1);                    // 拼接方法名                    String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);                    // 获取set方法对象                    Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName, String.class);                    // 使用set方法给对象obj属性赋值                    setMethod.invoke(obj, rs.getString(propertyName));                }            }        } catch (Exception e) {            e.printStackTrace();        }        return obj;    }    // 局部测试   47    public static void main(String[] args) {        String sql = "insert into t_user values(#{id},#{name},#{age})";        int fromIndex = 0;        int index = 1;        while(true){            int jingIndex = sql.indexOf("#", fromIndex);            if (jingIndex < 0) {                break;            }            System.out.println(index);            index++;            int youKuoHaoIndex = sql.indexOf("}", fromIndex);            String propertyName = sql.substring(jingIndex + 2, youKuoHaoIndex).trim();            System.out.println(propertyName);            fromIndex = youKuoHaoIndex + 1;        }    }    /**     * 提交事务   46     */    public void commit(){        factory.getTransaction().commit();    }    /**     * 回滚事务   46     */    public void rollback(){        factory.getTransaction().rollback();    }    /**     * 关闭事务   46     */    public void close(){        factory.getTransaction().close();    }}

insert方法实现46动态占位符传值47代码.god.ibatis.core46-47是SqlSession中的insertt

/**     * 执行insert语句,将记录插入数据库表。  46     * @param sqlId sql语句的id     * @param pojo 插入的数据。     * @return     */    public int insert(String sqlId, Object pojo){        int count = 0;        try {            // JDBC代码,执行insert语句,完成插入操作。            Connection connection = factory.getTransaction().getConnection();            // insert into t_user values(#{id},#{name},#{age})            String godbatisSql = factory.getMappedStatements().get(sqlId).getSql();            // insert into t_user(id,name,age) values(?,?,?,?)            String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?");            PreparedStatement ps = connection.prepareStatement(sql);            // 给?占位符传值    47            // 难度是什么:            // 第一:你不知道有多少人?            // 第二:你不知道在pojo对象中赋予哪个属性 ?            // ps.String(几个问号, 传什么值); // 这里都是setstring,所以数据库表中的字段类型要求都是varchar。这是godbatis失败的地方。            int fromIndex = 0;            int index = 1;            while(true){                int jingIndex = godbatisSql.indexOf("#", fromIndex);                if (jingIndex < 0) {                    break;                }                int youKuoHaoIndex = godbatisSql.indexOf("}", fromIndex);                String propertyName = godbatisSql.substring(jingIndex + 2, youKuoHaoIndex).trim();                fromIndex = youKuoHaoIndex + 1;                // 有属性名id,如何获得id的属性值?调用getid()方法                //拼方法名                String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);                ///使用反射机制根据方法名获得方法                Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName);                ///利用反射机制根据调用获得的方法获得属性值                Object propertyValue = getMethod.invoke(pojo);                //给?赋值                ps.setString(index, propertyValue.toString());                index++;            }            // 执行SQL语句            count = ps.executeUpdate();        } catch (Exception e) {            e.printStackTrace();        }        return count;    }

org局部测试代码.god.ibatis.core47是SqlSession中的main

// 局部测试   47    public static void main(String[] args) {        String sql = "insert into t_user values(#{id},#{name},#{age})";        int fromIndex = 0;        int index = 1;        while(true){            int jingIndex = sql.indexOf("#", fromIndex);            if (jingIndex < 0) {                break;            }            System.out.println(index);            index++;            int youKuoHaoIndex = sql.indexOf("}", fromIndex);            String propertyName = sql.substring(jingIndex + 2, youKuoHaoIndex).trim();            System.out.println(propertyName);            fromIndex = youKuoHaoIndex + 1;        }    }

12.第14步测试插入数据48代码.god.ibatis.test48是GodBatistestest中的方法testinsertuser

//测试插入数据  48    @Test    public void testInsertUser(){        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("godbatis-config.xml"));        SqlSession sqlSession = sqlSessionFactory.openSession();        // 执行SQL insert        User user = new User("1111", "zhangsan", "20");        int count = sqlSession.insert("user.insertUser", user);        System.out.println(count);        sqlSession.commit();        sqlSession.close();    }

手写mybatis框架1_mybatis_02

13.第十五步selectone方法在orgg中实现49-50代码.god.ibatis.test49-50是GodBatistest中的方法selectone

/**     * 执行查询句,返回一个对象。该方法仅适用于返回记录的sql语句。   49     * @param sqlId     * @param param     * @return     */    public Object selectOne(String sqlId, Object param){        Object obj = null;        try {            Connection connection = factory.getTransaction().getConnection();            MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId);            // 这是DQL查询的句子            // select * from t_user where id = #{id}            String godbatisSql = mappedStatement.getSql();            String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$*}, "?");            PreparedStatement ps = connection.prepareStatement(sql);            // 给占位符传值            ps.setString(1, param.toString());            // 查询返回结果集            ResultSet rs = ps.executeQuery();            // 包装的结果类型。            String resultType = mappedStatement.getResultType(); // org.god.ibatis.pojo.User            // 从结果中集中取数据,java对象的封装            if (rs.next()) {                //使用反射                // 获取resulttypeClass                Class resultTypeClass = Class.forName(resultType);                // 调用无参数构建方法创建对象                obj = resultTypeClass.newInstance(); // Object obj = new User();                // 给User类id,name,age属性赋值                // 赋予obj对象哪个属性哪个值。    50                /*                mysql> select * from t_user where id = '1111';                +------+----------+------+                | id   | name     | age  |                +------+----------+------+                | 1111 | zhangsan | 20   |                +------+----------+------+                解决问题的关键:将查询结果的列名作为属性名。                列名是id,所以属性名是:id                排名是name,所以属性名是:name                 */                ResultSetMetaData rsmd = rs.getMetaData();                int columnCount = rsmd.getColumnCount();                for (int i = 0; i < columnCount; i++) {                    //getcolumnname                    String propertyName = rsmd.getColumnName(i + 1);                    // 拼接方法名                    String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);                    // 获取set方法对象                    Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName, String.class);                    // 使用set方法给对象obj属性赋值                    setMethod.invoke(obj, rs.getString(propertyName));                }            }        } catch (Exception e) {            e.printStackTrace();        }        return obj;    }

14.第十七步最终版测试51代码在org.god.ibatis.test51是GodBatistestest中的方法testelectonet

///测试查询一个数据  51    @Test    public void testSelectOne(){        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("godbatis-config.xml"));        SqlSession sqlSession = sqlSessionFactory.openSession();        // 执行SQL语句        Object obj = sqlSession.selectOne("user.selectById", "1111");        System.out.println(obj);        sqlSession.close();    }

手写mybatis框架1_sql_03