首页 > 其他分享 >04. MyBatis的底层原理

04. MyBatis的底层原理

时间:2022-10-06 17:12:36浏览次数:59  
标签:return String 04 id sql MyBatis import public 底层

一、使用dom4j解析配置文件

1.1、引入依赖

<?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>star.light</groupId>
    <artifactId>mybatis</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.12</version>
        </dependency>
    </dependencies>
</project>

1.2、创建核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <!-- environments:配置多个连接数据库环境 -->
    <environments default="development">
        <!-- environment:配置某个具体的环境 -->
        <environment id="development">
            <!-- 设置事务管理方式 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <!-- 设置连接数据库的驱动 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!-- 设置连接数据库的地址 -->
                <property name="url" value="jdbc:mysql://localhost:3306/db_test"/>
                <!-- 设置连接数据库的用户名 -->
                <property name="username" value="root"/>
                <!-- 设置连接数据库的密码 -->
                <property name="password" value="abc123"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入映射文件-->
    <mappers>
        <!--
            指定 XxxMapper.xml 配置文件的路径
            resource属性自动会从类的根路径下开始查找资源
            url属性从绝对路径当中加载资源,一般很少使用,语法格式如下:file:///绝对路径
        -->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

1.3、创建映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="userMapper">

    <!-- 添加一条用户信息 -->
    <insert id="insertUser">
        insert into t_user(id,username,password,age,sex,email)
        values (#{id},#{username},#{password},#{age},#{sex},#{email})
    </insert>

    <!-- 根据id删除指定用户 -->
    <delete id="deleteUserById">
        delete from t_user where id = #{id}
    </delete>

    <!-- 根据id修改某条记录 -->
    <update id="updateUserById">
        update t_user set username = #{username},
                        password = #{password},
                        age = #{age},
                        sex = #{sex},
                        email = #{email}
        where id = #{id}
    </update>

    <!-- 根据id查询用户信息 -->
    <select id="selectUserById" resultType="star.light.pojo.User">
        select id,username,password,age,sex,email from t_user where id = #{id}
    </select>

    <!-- 查询所有用户信息 -->
    <select id="selectAllUser" resultType="star.light.pojo.User">
        select id,username,password,age,sex,email from t_user
    </select>
</mapper>

1.4、测试程序

package star.light.test;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

public class ParseByDom4jTest {

    @Test
    public void testParseMyBatisConfigXml() throws DocumentException {
        // 创建SAXReader对象
        SAXReader reader = new SAXReader();
        // 获取输入流
        InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("config.xml");
        // 读XML文件,但会Document对象,Document对象是文档对象,代表了整个XML文件
        Document document = reader.read(resourceAsStream);

        // 获取default默认环境id
        // 以下的xpath代表了:从根找<configuration>标签,然后找<configuration>标签下的子标签<environments>标签
        String xpath = "/configuration/environments";
        Element environments = (Element) document.selectSingleNode(xpath);
        // 获取属性的值
        String defaultEnvironmentId = environments.attributeValue("default");
        // 获取具体的环境environment
        xpath = "/configuration/environments/environment[@id='" + defaultEnvironmentId + "']";
        Element environment = (Element) document.selectSingleNode(xpath);
        // 获取enviroment节点下的transactionManager节点
        Element transactionManager = environment.element("transactionManager");
        String transactionType = transactionManager.attributeValue("type");
        System.out.println("事务管理器的类型:" + transactionType);
        // 获取dataSource节点
        Element dataSource = environment.element("dataSource");
        String dataSourceType = dataSource.attributeValue("type");
        System.out.println("数据源的类型:" + dataSourceType);
        // 获取dataSource节点下的所有子节点
        List<Element> propertyElements = dataSource.elements();
        propertyElements.forEach(propertyElement -> {
            String name = propertyElement.attributeValue("name");
            String value = propertyElement.attributeValue("value");
            System.out.println(name + "=" + value);
        });

        // 获取所有的<mapper>标签
        // 不从根下获取,从任意位置开始,获取所有的某个标签
        xpath = "//mapper";
        List<Node> mappers = document.selectNodes(xpath);
        // 遍历
        mappers.forEach(mapper -> {
            Element mapperElement = (Element) mapper;
            String resource = mapperElement.attributeValue("resource");
            System.out.println("Mapper映射文件的路径:" + resource);
        });
    }

    @Test
    public void testParseSqlMapperXML() throws DocumentException {
        SAXReader reader = new SAXReader();
        InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("UserMapper.xml");
        Document document = reader.read(inputStream);
        // 获取namespace
        String xpath = "/mapper";
        Element mapper = (Element) document.selectSingleNode(xpath);
        String namespace = mapper.attributeValue("namespace");
        System.out.println("namespace = " + namespace);
        // 获取mapper节点下所有子节点
        List<Element> elements = mapper.elements();
        elements.forEach(element -> {
            // 获取SQLId
            String id = element.attributeValue("id");
            System.out.println("id = " + id);
            // 获取resultType,没有这个属性的话,会自动返沪"null"
            String resultType = element.attributeValue("resultType");
            System.out.println("resultType = " + resultType);
            // 获取标签中的SQL语句(表示获取标签中的文本内容,而且去除前后空白)
            String sql = element.getTextTrim();
            String newSQL = sql.replaceAll("#\\{[0-9A-Za-z_$]*}","?");
            System.out.println(newSQL);
        });
    }
}

二、MyBatis框架的实现

2.1、引入依赖

<?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>star.light</groupId>
    <artifactId>mybatis</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>
    </dependencies>
</project>

2.2、创建核心类

  SqlSessionFactoryBuilder:

package star.light.frame.core;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import star.light.frame.core.constant.Const;
import star.light.frame.core.datasource.JndiDataSource;
import star.light.frame.core.datasource.PoolDataSource;
import star.light.frame.core.datasource.UnPooledDataSource;
import star.light.frame.core.mapper.MappedStatement;
import star.light.frame.core.transaction.Transaction;
import star.light.frame.core.transaction.impl.JdbcTransaction;
import star.light.frame.core.transaction.impl.ManagedTransaction;
import star.light.frame.util.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构建其对象
 * 通过SqlSessionFactoryBuilder的build()来解析核心配置文件,创建SqlSessionFactory对象
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactoryBuilder() {
    }

    /**
     * 解析核心配置文件,来构建SqlSessionFactory对象
     * @param inputStream 指向核心配置文件的一个输入流
     * @return SqlSessionFactory对象
     */
    public SqlSessionFactory build(InputStream inputStream){
        SqlSessionFactory sqlSessionFactory = null;

        try {
            // 解析核心配置文件
            SAXReader reader = new SAXReader();
            // 读XML文件,但会Document对象,Document对象是文档对象,代表了整个XML文件
            Document document = reader.read(inputStream);
            // 从根找<configuration>标签,然后找<configuration>标签下的子标签<environments>标签
            Element environments = (Element) document.selectSingleNode("/configuration/environments");
            // 获取default默认环境id
            String defaultId = environments.attributeValue("default");
            // 获取默认的环境
            Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");
            // 获取事务管理器节点
            Element transactionManagerElement = environment.element("transactionManager");
            // 获取数据源节点
            Element dataSourceElement = environment.element("dataSource");
            // 存放SqlMapper.xml文件的路径的集合
            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(dataSourceElement);
            // 获取事务管理器对象
            Transaction transaction = getTransaction(transactionManagerElement,dataSource);
            // 获取mappedStatements
            Map<String, MappedStatement> mappedStatement = getMappedStatements(sqlMapperXmlPathList);

            // 解析完成之后,构建SqlSessionFactory对象
            sqlSessionFactory = new SqlSessionFactory(transaction,mappedStatement);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sqlSessionFactory;
    }


    /**
     * 获取数据源对象
     * @param dataSourceElement 数据源标签元素
     * @return
     */
    private DataSource getDataSource(Element dataSourceElement) {
        DataSource dataSource = null;
        String type = dataSourceElement.attributeValue("type").trim().toUpperCase();
        Map<String,String> map = new HashMap<>();

        // 获取所有的property
        List<Element> propertyElements = dataSourceElement.elements("property");
        propertyElements.forEach(propertyElement -> {
            String name = propertyElement.attributeValue("name");
            String value = propertyElement.attributeValue("value");
            map.put(name,value);
        });

        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 PoolDataSource();
        }
        if (Const.JNDI_DATASOURCE.equals(type)) {
            dataSource = new JndiDataSource();
        }
        return dataSource;
    }

    /**
     * 获取事务管理器对象
     * @param transactionManagerElement 事务管理器标签元素
     * @param dataSource 数据源对象
     * @return
     */
    private Transaction getTransaction(Element transactionManagerElement, DataSource dataSource) {
        String type = transactionManagerElement.attributeValue("type").trim().toUpperCase();
        Transaction transaction = null;

        if (Const.JDBC_TRANSACTION.equals(type)) {
            // 默认开启事务,将来是要手动提交
            transaction = new JdbcTransaction(dataSource,false);
        }
        if (Const.MANAGED_TRANSACTION.equals(type)) {
            transaction = new ManagedTransaction();
        }
        return transaction;
    }

    /**
     * 解析所有的SqlMapper.xml配置文件,构造Map集合
     * @param sqlMapperXmlPathList
     * @return
     */
    private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXmlPathList) {
        Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
        sqlMapperXmlPathList.forEach(sqlMapperXmlPath -> {
            try {
                SAXReader reader = new SAXReader();
                // SqlMapper配置文件
                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 -> {
                    // 获取sql的id
                    String id = element.attributeValue("id");
                    String sqlId = namespace + "." + id;
                    // 获取resultType
                    String resultType = element.attributeValue("resultType");
                    // 获取SQL语句
                    String sql = element.getTextTrim();
                    MappedStatement mappedStatement = new MappedStatement(sql, resultType);
                    mappedStatementMap.put(sqlId,mappedStatement);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        return mappedStatementMap;
    }
}

  SqlSessionFactory:

package star.light.frame.core;

import star.light.frame.core.mapper.MappedStatement;
import star.light.frame.core.transaction.Transaction;

import java.util.Map;

/**
 * 一个数据库一般对应一个SqlSessionFactory对象
 * 通过SqlSessionFactory对象可以获取SalSession对象(开启会话)
 * 一个SqlSessionFactory对象可以开启多个SqlSession会话
 */
public class SqlSessionFactory {
    /**
     * 事务管理器属性
     * 事务管理器是可以灵活切换的
     * SqlSessionFactory类中的事务管理器应该是面向接口编程的
     * SqlSessionFactory类中应该有一个事务管理器接口
     */
    private Transaction transaction;

    // 存放SQL语句的Map集合,key是sqlId,value是对应的SQL标签信息对象
    private Map<String, MappedStatement> mappedStatementMap;

    public SqlSessionFactory() {
    }

    public SqlSessionFactory(Transaction transaction, Map<String, MappedStatement> mappedStatementMap) {
        this.transaction = transaction;
        this.mappedStatementMap = mappedStatementMap;
    }

    public Transaction getTransaction() {
        return transaction;
    }

    public void setTransaction(Transaction transaction) {
        this.transaction = transaction;
    }

    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }

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

    /**
     * 获取Session会话对象
     * @return
     */
    public SqlSession openSession(){
        // 开启会话的前提是开启连接
        transaction.openConnection();
        // 创建SqlSession对象
        SqlSession sqlSession = new SqlSession(this);

        return sqlSession;
    }
}

  SqlSession:

package star.light.frame.core;

import star.light.frame.core.mapper.MappedStatement;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;

/**
 * 专门负责执行SQL语句的会话对象
 */
public class SqlSession {
    private SqlSessionFactory sqlSessionFactory;

    public SqlSession(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    /**
     * 执行insert语句向数据库表中插入记录
     * @param sqlId SQL语句的id
     * @param pojo 插入的数据
     * @return
     */
    public int insert(String sqlId,Object pojo){
        int affectedRowCount = 0;

        try {
            // JDBC代码,执行insert语句,完成插入
            Connection connection = sqlSessionFactory.getTransaction().getConnection();
            // 获取映射文件中的SQL语句,insert into 表名(字段名1,字段名2,...) values(#{字段值1},#{字段值2},...)
            String frameSql = sqlSessionFactory.getMappedStatementMap().get(sqlId).getSql();
            // 替换后的SQL语句,insert into 表名(字段名1,字段名2,...) values(?,?,...)
            String sql = frameSql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?");
            // 编译SQL语句
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            // 给占位符传值
            // 不知道占位符的个数
            // 不知道该将pojo对象中的哪个属性赋值给哪个占位符
            // 不知道pojo对象属性的类型
            int fromIndex = 0;
            int index = 1;      // 占位符的下标
            while (true){
                int targetStartIndex = frameSql.indexOf("#",fromIndex);
                if (targetStartIndex < 0){
                    break;
                }
                int targetEndIndex = frameSql.indexOf("}",targetStartIndex);
                String propertyName = frameSql.substring(targetStartIndex+2,targetEndIndex).trim();
                fromIndex = targetEndIndex + 1;
                // 有属性名id,调用getId()获取id的属性值
                String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName);
                Object propertyValue = getMethod.invoke(pojo);
                preparedStatement.setObject(index,propertyValue);
                index++;
            }

            // 执行SQL语句
            affectedRowCount = preparedStatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return affectedRowCount;
    }

    /**
     * 执行查询语句返回一个对象,该方法只适合返回一条记录的SQL语句
     * @param sqlId SQL语句的id
     * @param param
     * @return
     */
    public Object selectOne(String sqlId,Object param){
        Object object = null;

        try {
            // 获取数据库连接
            Connection connection = sqlSessionFactory.getTransaction().getConnection();
            MappedStatement mappedStatement = sqlSessionFactory.getMappedStatementMap().get(sqlId);
            // 获取sql语句 select 字段名1,字段名2,... from 表名 where 字段名 = ${字段值}
            String frameSql = mappedStatement.getSql();
            // 替换后的SQL语句,select 字段名1,字段名2,... from 表名 where 字段名 = ?
            String sql = frameSql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?");
            // 编译SQL语句
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            // 给占位符传值
            preparedStatement.setObject(1,param);
            // 执行查询,返回结果集
            ResultSet resultSet = preparedStatement.executeQuery();
            // 要封装的结果类型
            String resultType = mappedStatement.getResultType();
            // 从结果集中取数据封装java对象
            if (resultSet.next()) {
                // 获取resultType的Class
                Class<?> resultTypeClass = Class.forName(resultType);
                // 调用无参的构造方法创建对象
                object = resultTypeClass.getConstructor().newInstance();
                // 给属性赋值
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for (int i = 0; i < columnCount; i++) {
                    String propertyName = metaData.getColumnLabel(i + 1);
                    Object propertyValue = resultSet.getObject(propertyName);
                    Field field = resultTypeClass.getDeclaredField(propertyName);
                    field.setAccessible(true);
                    field.set(object, propertyValue);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return object;
    }

    public void commit(){
        sqlSessionFactory.getTransaction().commit();
    }

    public void rollback(){
        sqlSessionFactory.getTransaction().rollback();
    }

    public void close(){
        sqlSessionFactory.getTransaction().close();
    }
}

2.3、数据源的实现类

  UNPOOLED 类型的数据源:

package star.light.frame.core.datasource;

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
 * 不使用连接池每一都新建连接对象
 */
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) {
            e.printStackTrace();
        }
        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 {
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }

    @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> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

  POOLED 类型的数据源:

package star.light.frame.core.datasource;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * 数据源的实现类:POOLED
 * 使用框架内置的数据库连接池来获取Connection对象
 * 这里就不实现了
 */
public class PoolDataSource 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> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

  JNDI 类型的数据源

package star.light.frame.core.datasource;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * 数据源的实现类:JNDI
 * 使用第三方的数据库连接池获取Connection对象
 * 这里就不实现了
 */
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> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

2.4、事务管理器

  事务管理器接口:

package star.light.frame.core.transaction;

import java.sql.Connection;

/**
 * 事务管理器接口
 * 所有的事务管理器都应该遵循该规范
 * JDBC事务管理器、MANAGED事务管理器都应该实现这个接口
 * Transaction事务管理器提供控制事务的方法
 */
public interface Transaction {
    void commit();                  // 提交事务
    void rollback();                // 回滚事务
    void close();                   // 关闭事务
    void openConnection();          // 开启数据库连接
    Connection getConnection();     // 获取数据库连接对象
}

  JDBC事务管理器:

package star.light.frame.core.transaction.impl;

import star.light.frame.core.transaction.Transaction;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * JDBC事务管理器
 */
public class JdbcTransaction implements Transaction {

    /**
     * 数据源属性
     * 面向接口编程
     */
    private DataSource dataSource;

    /**
     * 自动提交标记
     * true表示自动提交
     * false表示不采用自动提交
     */
    private boolean autoCommit;

    // 连接对象
    private Connection connection;

    /**
     * 创建事务管理器对象
     * @param dataSource
     * @param autoCommit
     */
    public JdbcTransaction(DataSource dataSource, boolean autoCommit) {
        this.dataSource = dataSource;
        this.autoCommit = autoCommit;
    }

    @Override
    public void openConnection(){
        if (connection == null) {
            try {
                connection = dataSource.getConnection();
                // 开启事务
                connection.setAutoCommit(autoCommit);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() {
        return connection;
    }

    @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();
        }
    }
}

  MANAGED事务管理器:

package star.light.frame.core.transaction.impl;

import star.light.frame.core.transaction.Transaction;

import java.sql.Connection;

/**
 * MANAGED事务管理器
 * 这里就不实现了
 */
public class ManagedTransaction implements Transaction {
    @Override
    public void openConnection() {

    }

    @Override
    public Connection getConnection() {
        return null;
    }

    @Override
    public void commit() {

    }

    @Override
    public void rollback() {

    }

    @Override
    public void close() {

    }
}

2.5、SQL标签的类

package star.light.frame.core.mapper;

/**
 * 普通的Java类,封装了一个SQL标签
 * 一个MappedStatement对象对应一个SQL标签
 * 一个SQL标签的所有信息封装到MappedStatement对象中
 */
public class MappedStatement {
    // SQL语句
    private String sql;
    /**
     * 要封装的结果集类型
     * insert、delete、update语句的时候,resultType是null
     * 只有当sql语句是select语句的时候resultType才有值
     */
    private String resultType;

    public MappedStatement() {
    }

    public MappedStatement(String sql, String resultType) {
        this.sql = sql;
        this.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;
    }

    @Override
    public String toString() {
        return "MappedStatement{" +
                "sql='" + sql + '\'' +
                ", resultType='" + resultType + '\'' +
                '}';
    }
}

2.6、常量类

package star.light.frame.core.constant;

/**
 * 整个框架的常量类
 */
public class Const {
    public static final String UN_POOLED_DATASOURCE = "UNPOOLED";
    public static final String POOLED_DATASOURCE = "POOLED";
    public static final String JNDI_DATASOURCE = "JNDI";
    public static final String JDBC_TRANSACTION = "JDBC";
    public static final String MANAGED_TRANSACTION = "MANAGED";
}

2.7、工具类

package star.light.frame.util;

import java.io.InputStream;

/**
 * 工具类专门完成“类路径”的加载
 */
public class Resources {

    /**
     * 工具类的构造方法都是建议私有化的
     * 因为工具类中的方法都是静态的,不需要创建对象就行调用
     * 为了避免new对象,建议所有构造方法私有化
     */
    public Resources() {}

    /**
     * 从路径中加载资源
     * @param inputstream 放在类路径当中的资源文件
     * @return 指向资源文件的一个输入流
     */
    public static InputStream getResourceAsStream(String inputstream){
        return ClassLoader.getSystemClassLoader().getResourceAsStream(inputstream);
    }
}

2.8、打包

image

三、框架的测试

3.1、创建数据库

CREATE DATABASE IF NOT EXISTS db_test;
USE db_test;

CREATE TABLE IF NOT EXISTS t_user(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(20),
	password VARCHAR(20),
	age INT,
	sex VARCHAR(10),
	email VARCHAR(30)
); 

3.2、引入依赖

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>

<!-- junit依赖 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

3.3、创建核心配置文件

  核心配置文件的取命随意,这里取命为 config.xml,存放在类的根目录下;

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

    <!-- environments:配置多个连接数据库环境 -->
    <environments default="development">
        <!-- environment:配置某个具体的环境 -->
        <environment id="development">
            <!-- 设置事务管理方式 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 -->
            <dataSource type="UNPOOLED">
                <!-- 设置连接数据库的驱动 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!-- 设置连接数据库的地址 -->
                <property name="url" value="jdbc:mysql://localhost:3306/db_test"/>
                <!-- 设置连接数据库的用户名 -->
                <property name="username" value="root"/>
                <!-- 设置连接数据库的密码 -->
                <property name="password" value="27185.Sakura"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入映射文件-->
    <mappers>
        <!--
            指定 XxxMapper.xml 配置文件的路径
            resource属性自动会从类的根路径下开始查找资源
            url属性从绝对路径当中加载资源,一般很少使用,语法格式如下:file:///绝对路径
        -->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

3.4、创建实体类

package star.light.pojo;

public class User {
    // 数据库表当中的字段应该和pojo类的属性一一对应
    // 建议使用包装类,这样可以防止从数据库查出字段的值为null的问题
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private String sex;
    private String email;

    public User() {
    }

    public User(Integer id, String username, String password, Integer age, String sex, String email) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
        this.sex = sex;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

3.5、创建映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="userMapper">

    <!-- 添加一条用户信息 -->
    <insert id="insertUser">
        insert into t_user(id,username,password,age,sex,email)
        values (#{id},#{username},#{password},#{age},#{sex},#{email})
    </insert>


    <!-- 根据id查询用户信息 -->
    <select id="selectUserById" resultType="star.light.pojo.User">
        select id,username,password,age,sex,email from t_user where id = #{id}
    </select>
</mapper>

3.6、测试程序

package star.light.test;

import org.junit.Test;
import star.light.frame.core.SqlSession;
import star.light.frame.core.SqlSessionFactory;
import star.light.frame.core.SqlSessionFactoryBuilder;
import star.light.frame.util.Resources;
import star.light.pojo.User;

public class MyFrameTest {

    @Test
    public void testInsertUser(){
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User(null,"Sakura","Sakura",10,"女","[email protected]");
        int affectedRowCount = sqlSession.insert("userMapper.insertUser", user);
        System.out.println("affectedRowCount = " + affectedRowCount);
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void testSelectById(){
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Object object = sqlSession.selectOne("userMapper.selectUserById", 1);
        System.out.println(object);
    }
}

标签:return,String,04,id,sql,MyBatis,import,public,底层
From: https://www.cnblogs.com/nanoha/p/16758016.html

相关文章