Featured image of post Java工程师 MyBatis编码实现

Java工程师 MyBatis编码实现

🌏Java工程师 MyBatis编码实现 🎯 这篇文章用于记录 对MyBatis的原理实现实践

🎄MyBatis核心流程

🎄环境搭建

创建Maven项目

引入需要的依赖

<dependencies>
    <!--引入dom4j 解析xml文件-->
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <!--引入mysql依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <!--lombok 简化entity开发 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.4</version>
    </dependency>
    <!--junit依赖-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

创建表和实体类

创建DuckMapper数据访问接口类

package com.bbm.mapper;
import com.bbm.entity.Duck;
/**
 @author Liu Xianmeng
 @createTime 2023/10/14 11:34
 @instruction 鸭子数据访问接口
 */
public interface DuckMapper {
    //查询方法
    public Duck getDuckById(Integer id);
}

创建映射sql文件

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.bbm.mapper.DuckMapper">
    <!--实现配置接口方法getDuckById-->
    <select id="getDuckById" resultType="com.bbm.entity.Duck">
        select * from duck where id = ?
    </select>
</mapper>

创建mybatis的配置文件bigbigmeng-mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<database>
    <!-- 配置连接数据库的信息 -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/employees?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</database>

🎄实现MyBatis

Function类实现

创建Function类 对应DuckMapper.xml文件中的方法

package com.bbm.mybatis.config;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:18
@instruction
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Function {
    private String sqlType; // sql类型 比如select insert update delete
    private String funcName; // 方法名
    private String sql; // 执行的sql语句
    private Object resultType; // 返回类型
    private String parameterType; // 参数类型
}

MapperBean类实现

package com.bbm.mybatis.config;

/**
@author Liu Xianmeng
@createTime 2023/10/14 12:22
@instruction 将mapper数据访问接口类DuckMapper的信息进行封装
             如接口类名、接口中的所有方法
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MapperBean {
    //接口名
    private String interfaceName;
    //接口下的所有方法-集合
    private List<Function> functions;
}

MyBatisSqlSessionConfig类实现

创建MyBatis的配置类MyBatisSqlSessionConfiguration 读取xml配置文件 -> 创建数据库的连接

package com.bbm.mybatis.sqlSession;

/**
@author Liu Xianmeng
@createTime 2023/10/14 12:16
@instruction 读取bigbigmeng-mybatis.xml文件 创建数据库连接
*/
public class MyBatisSqlSessionConfig {
    // 类的加载器
    private static ClassLoader loader = ClassLoader.getSystemClassLoader();

    /**
     * 读取数据源信息 并返回数据库连接
     * @param resource
     * @return
     */
    public Connection build(String resource) {
        // 先创建一个空的sql连接
        Connection connection = null;
        try {
            // 加载bigbigmeng-mybatis.xml文件 获取到对应的InputStream
            InputStream stream = loader.getResourceAsStream(resource);
            // 解析bigbigmeng-mybatis.xml文件  => dom4j
            SAXReader reader = new SAXReader();
            Document document = reader.read(stream);
            //获取到bigbigmeng-mybatis.xml文件 的根元素 <database>
            Element root = document.getRootElement();
            System.out.println("C MyBatisSqlSessionConfig M build() -> root=" + root);
            //解析root元素 返回Connection 这个readDataSource在下面单独写
            connection = readDataSource(root);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 这个方法会解析bigbigmeng-mybatis.xml 并返回Connection数据库连接
     * @param element
     * @return
     */
    private Connection readDataSource(Element element) {
        // 根节点的名字应该是 "database"
        if (!"database".equals(element.getName())) {
            throw new RuntimeException("root节点应该是<database>");
        }
        // 连接DB的必要属性参数
        String driverClassName = null;
        String url = null;
        String username = null;
        String password = null;
        // 遍历node下的子节点 获取属性值
        for (Object item : element.elements("property")) {
            // 获取数据库连接的属性
            Element property = (Element) item;
            String name = property.attributeValue("name");
            String value = property.attributeValue("value");
            //判断是否得到name和value
            if (name == null || value == null) {
                throw new RuntimeException("property节点没有设置name或者value属性");
            }
            // 进行参数设置
            switch (name) {
                case "url":
                    url = value;
                    break;
                case "username":
                    username = value;
                    break;
                case "driverClassName":
                    driverClassName = value;
                    break;
                case "password":
                    password = value;
                    break;
                default:
                    throw new RuntimeException("属性名没有匹配到...");
            }
        }
        // 先创建一个空的连接
        Connection connection = null;
        try {
            // 通过JDBC获取数据库连接
            Class.forName(driverClassName);
            connection = DriverManager.getConnection(url,username,password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回Connection
        return connection;
    }

    /**
     * 根据传入的mapper映射文件的路径 找到对应的mapper.xml文件 读取并构建对应的MapperBean对象
     * @param path
     * @return 返回MapperBean对象
     */
    public MapperBean readMapper(String path) {
        MapperBean mapperBean = new MapperBean();
        try {
            // 获取到xml文件对应的InputStream
            InputStream stream = loader.getResourceAsStream(path);
            SAXReader reader = new SAXReader();
            // 获取到xml文件对应的document-dom4j
            Document document = reader.read(stream);
            /* 得到xml文件的根元素/根节点
               <mapper namespace="com.bbm.mapper.DuckMapper">
                    <!--实现配置接口方法getMonsterById-->
                    <select id="getDuckById" resultType="com.bbm.entity.Duck">
                        select * from duck where id = ?
                    </select>
                </mapper>
             */
            Element root = document.getRootElement();
            // 获取到namespace -> com.bbm.mapper.DuckMapper
            String namespace = root.attributeValue("namespace").trim();
            // 设置mapperBean的属性interfaceName
            mapperBean.setInterfaceName(namespace);
            // 得到root的迭代器 可以遍历它的子节点/子元素 生成Function
            Iterator rootIterator = root.elementIterator();
            // 保存接口下所有的方法信息
            List<Function> list = new ArrayList<>();
            // 遍历它的子节点/子元素 生成Function
            while (rootIterator.hasNext()) {
                // 取出一个子元素 dom4j Element
                /*
                    <!--实现配置接口方法getDuckById-->
                    <select id="getDuckById" resultType="com.bbm.entity.Duck">
                        select * from duck where id = ?
                    </select>
                 */
                Element e = (Element) rootIterator.next();
                Function function = new Function();
                String sqlType = e.getName().trim();
                String funcName = e.attributeValue("id").trim();
                // resultType是返回类型的全路径 即全类名
                String resultType = e.attributeValue("resultType").trim();
                String sql = e.getText().trim();
                // 开始封装
                function.setSql(sql);
                function.setFuncName(funcName);
                function.setSqlType(sqlType);
                // 使用反射生成一个返回类型对象 并setResultType
                Object newInstance = Class.forName(resultType).newInstance();
                function.setResultType(newInstance);

                // 将封装好的function对象加入到 list
                list.add(function);
            }
            // while循环结束后 将function的list设置
            mapperBean.setFunctions(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mapperBean;
    }
}

创建Executor执行器接口

package com.bbm.mybatis.sqlSession;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:47
@instruction
*/

public interface Executor {
    //泛型方法
    public <T> T query(String statement, Object parameter);
}

创建Executor执行器实现类

package com.bbm.mybatis.sqlSession;

/**
@author Liu Xianmeng
@createTime 2023/10/14 12:48
@instruction
*/
public class BBMExecutor implements Executor {

    // 持有一个数据源配置对象
    private MyBatisSqlSessionConfig myBatisSqlSessionConfig = new MyBatisSqlSessionConfig();

    // 编写方法 通过MyBatisSqlSessionConfig对象 返回连接
    private Connection getConnection() {
        Connection connection = myBatisSqlSessionConfig.build("mapper/hsp_mybatis.xml");
        return connection;
    }

    @Override
    public <T> T query(String statement, Object parameter) {
        //得到连接Connection
        Connection connection = getConnection();
        //查询返回的结果集
        ResultSet set = null;
        PreparedStatement pre = null;

        try {
            pre = connection.prepareStatement(statement);
            // 设置参数 如果参数多 可以使用数组处理
            pre.setString(1, parameter.toString());
            set = pre.executeQuery();
            // set数据封装到对象duck
            // 这里做简化处理 -> 认为返回的结果就是一个monster记录 完善的写法是一套jdbc反射机制
            Duck duck = new Duck();

            // 遍历结果集 把数据封装到duck对象
            while (set.next()) {
                duck.setId(set.getInt("id"));
                duck.setName(set.getString("name"));
                duck.setAge(set.getInt("age"));
                duck.setBirthday(set.getDate("birthday"));
            }
            return (T) duck;

        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                if (set != null) {
                    set.close();
                }
                if (pre != null) {
                    pre.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (Exception throwables) {
                throwables.printStackTrace();
            }
        }
        return null;
    }
}