手写MyBatis 重要基本原理框架

news2024/12/28 21:00:16

1. 手写MyBatis 重要基本原理框架

文章目录

  • 1. 手写MyBatis 重要基本原理框架
    • 1.1 第一步:IDEA中创建模块
    • 1.2 第二步:资源工具类,方便获取指向配置文件的输入流
    • 1.3 第三步:定义SqlSessionFactoryBuilder类
    • 1.4 第四步:分析SqlSessionFactory类中有哪些属性
    • 1.5 第五步:定义JDBCTransaction
    • 1.6 第六步:事务管理器中需要数据源,定义UNPOOLEDDataSource
    • 1.7 第七步:定义一个MappedStatement 类用于存放 SQL 标签
    • 1.8 第八步:完善SqlSessionFactory类
    • 1.9 第九步:完善SqlSessionFactoryBuilder中的build方法
    • 1.10 第十步:编写SqlSession类中commit rollback close方法
    • 1.11 第十一步:编写SqlSession类中的insert方法
    • 1.12 第十二步:编写SqlSession类中的selectOne方法
  • 2. 将我们自己手写的MyBatiks 名为“godbatis”的框架,使用Maven打包
  • 3. 使用我们自己手写的 MyBatis 名为 “godbatis” 的框架,运行测试
  • 4. 总结:
  • 5. 最后:


这里我们手写MyBatis 就仅仅只实现 insert() 和 selectOne(单条记录的查询)操作,并且有些局限就是,数据库的所有字段类型,都必须是 varchar 类型才行。

手写框架之前,如果没有思路,可以先参考一下mybatis的客户端程序,通过客户端程序来逆推需要的类,参考代码:

@Test
public void testInsert(){
    SqlSession sqlSession = null;
    try {
        // 1.创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 2.创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        // 3.创建SqlSession对象
        sqlSession = sqlSessionFactory.openSession();
        // 4.执行SQL
        Car car = new Car(null, "111", "宝马X7", "70.3", "2010-10-11", "燃油车");
        int count = sqlSession.insert("insertCar",car);
        System.out.println("更新了几条记录:" + count);
        // 5.提交
        sqlSession.commit();
    } catch (Exception e) {
        // 回滚
        if (sqlSession != null) {
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        // 6.关闭
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

@Test
public void testSelectOne(){
    SqlSession sqlSession = null;
    try {
        // 1.创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 2.创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        // 3.创建SqlSession对象
        sqlSession = sqlSessionFactory.openSession();
        // 4.执行SQL
        Car car = (Car)sqlSession.selectOne("selectCarByCarNum", "111");
        System.out.println(car);
        // 5.提交
        sqlSession.commit();
    } catch (Exception e) {
        // 回滚
        if (sqlSession != null) {
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        // 6.关闭
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

1.1 第一步:IDEA中创建模块

模块:godbatis(创建普通的Java Maven模块,打包方式jar),引入相关依赖

在这里插入图片描述

<?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>godbatis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <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>
        <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>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>

1.2 第二步:资源工具类,方便获取指向配置文件的输入流

和MyBatis 框架一样,这里我们创建一个 Resoures 工具类,用来获取配置文件的输入流对象。

工具类的构造方法都是建议私有化的
因为工具类中的方法都是静态的,不需要创建对象就能调用
为了避免new对象,所有构造方法私有化
这只是一种编程习惯

在这里插入图片描述

package org.god.ibatis.utils;


import java.io.InputStream;

/**
 * godbatis 框架提供的一个工具类
 * 这个工具类专门完成“类路径” 中资源的加载
 */
public class Resources {

    /**
     * 工具类的构造方法都是建议私有化的
     * 因为工具类中的方法都是静态的,不需要创建对象就能调用
     * 为了避免new对象,所有构造方法私有化
     * 这只是一种编程习惯
     */

    private Resources() {}


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

1.3 第三步:定义SqlSessionFactoryBuilder类

提供一个无参数构造方法,再提供一个build方法,该build方法要返回SqlSessionFactory对象

package org.god.core;

import java.io.InputStream;


public class SqlSessionFactoryBuilder {

    /**
     * 创建构建器对象
     */
    public SqlSessionFactoryBuilder() {
    }


    /**
     * 获取SqlSessionFactory对象
     * 该方法主要功能是:读取godbatis核心配置文件,并构建SqlSessionFactory对象
     * @param inputStream 指向核心配置文件的输入流
     * @return SqlSessionFactory对象
     */
    public SqlSessionFactory build(InputStream inputStream){
        // 解析配置文件,创建数据源对象
        // 解析配置文件,创建事务管理器对象
        // 解析配置文件,获取所有的SQL映射对象
        // 将以上信息封装到SqlSessionFactory对象中
        // 返回
        return null;
    }
}

1.4 第四步:分析SqlSessionFactory类中有哪些属性

  • 事务管理器

    • GodJDBCTransaction
  • SQL映射对象集合

    • Map<String, GodMappedStatement>

1.5 第五步:定义JDBCTransaction

事务管理器最好是定义一个接口,然后每一个具体的事务管理器都实现这个接口。

在这里插入图片描述

package org.god.ibatis.core;


import java.sql.Connection;

/**
 * 事务管理接口
 * 所有的事务管理器都应该遵循该规范
 * JDBC 事务管理器,MANAGED 事务管理器都应该实现这个接口
 * Transaction事务管理器,提供管理事务方法。
 */
public interface Transaction {

    /**
     * 提交事务
     */
    void commit();


    /**
     * 回滚事务
     */
    void rollback();


    /**
     * 关闭事务
     */
    void close();

    /**
     * 真正的开启数据库连接
     */
    void openConnection();

    /**
     * 获取数据库连接对象的
     */
    Connection getConnection();

}

关于MyBatis 的事务,在 MyBatis 中有两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”):,但是这里我们只实现JDBC,这个值的事务。其他另外一个就不实现了。

在这里插入图片描述

package org.god.ibatis.core;


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

/**
 * JDBC事务管理器(godbatis 框架目前只有JdbcTransaction 进行实现)
 */
public class JdbcTransaction implements Transaction{
    /**
     * 数据源属性
     * 经典的设计:面向接口编程
     */
    private DataSource dataSource;


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


    /**
     * 连接对象
     */
    private Connection connection;

    public Connection getConnection() {
        return connection;
    }

    public void setConnection(Connection connection) {
        this.connection = 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();
        }

    }

    public void openConnection() {
        if (connection == null) {
            try {
                this.connection = dataSource.getConnection();
                connection.setAutoCommit(autoCommit);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

package org.god.ibatis.core;

import java.sql.Connection;

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;
    }
}

1.6 第六步:事务管理器中需要数据源,定义UNPOOLEDDataSource

在MyBatis中有三种内建的数据源类型(也就是 type=“[UNPOOLED|POOLED|JNDI]”),这里我们就实现UNPOOLED 这个值,另外两个值,就不实现了。

数据源是获取connection对象的
POOlED UNPOOLED JNDI
所有的数据源都要实现 JDK带的规范,javax.sql.DataSource

在这里插入图片描述

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
 * 不使用连接池,每一次都新建Connection对象
 */
public class UnPooledDataSource implements javax.sql.DataSource {

    private String driver;
    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.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    @Override
    public Connection getConnection() throws SQLException {
        // 这个连接池godbatis框架可以自己写一个连接池
        // 从数据库连接池当中获取Connection对象。(这个数据库练级吃是我godbatins框架内部封装好的。)
        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> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

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

在这里插入图片描述

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
 * 使用godbatis 框架内置的数据库连接池来获取Connection对象。(这个不实现)
 */
public class PooledDataSource 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;
    }
}

在这里插入图片描述

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
 * 使用godbatis 框架内置的数据库连接池来获取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;
    }
}

1.7 第七步:定义一个MappedStatement 类用于存放 SQL 标签

  • 普通的Java类,POJO,封装了一个SQL标签
  • 一个MappedStatement 对象对应一个SQL标签
  • 一个SQL标签中的所有信息封装到MappedStatement对象当中
  • 面向对象编程思想

在这里插入图片描述

package org.god.ibatis.core;


/**
 * 普通的Java类,POJO,封装了一个SQL标签
 * 一个MappedStatement 对象对应一个SQL标签
 * 一个SQL标签中的所有信息封装到MappedStatement对象当中
 * 面向对象编程思想
 */
public class MappedStatement {


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

    /**
     * 要封装的结果集类型,有的时候 resultType 是 null
     * 比如:insert,delete,update 语句的时候resultType是null
     * 只有当 sql语句 select 语句的时候 resultType 才有值。
     */
    private String resultType;

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

1.8 第八步:完善SqlSessionFactory类

SqlSessionFactory对象;

  • 一个数据库对应一个SqlSessionFactory对象
  • 通过SqlSessionFactory对象可以获取SqlSession对象(开启会话)
  • 一个SqlSessionFactory 对象可以开启对哦个SqlSession 会话

在这里插入图片描述

在SqlSessionFactory中添加openSession方法

package org.god.ibatis.core;



import java.util.List;
import java.util.Map;

/**
 * SqlSessionFactory对象;
 * 一个数据库对应一个SqlSessionFactory对象
 * 通过SqlSessionFactory对象可以获取SqlSession对象(开启会话)
 * 一个SqlSessionFactory 对象可以开启对哦个SqlSession 会话
 */
public class SqlSessionFactory {


    /**
     * 获取Sql会话对象
     * @return
     */
    public SqlSession openSession() {

        // 开启会话的前提是开启连接
        transaction.openConnection();
        // 创建SqlSession 对象
        SqlSession sqlSession = new SqlSession(this);
        
        return sqlSession;


    }
    public SqlSessionFactory() {
    }


    /**
     * 事务管理属性
     * 事务管理器可以灵活切换的
     * SqlSessionFactory类中的事务管理器应该是面向接口编程的
     * SqlSessionFactory类中的应该有一个事务管理器接口
     */
     private Transaction transaction;



     /**
     * 数据源属性
     */


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


    public Map<String, MappedStatement> getMappedStatements() {
        return mappedStatements;
    }

    public void setMappedStatements(Map<String, MappedStatement> mappedStatements) {
        this.mappedStatements = this.getMappedStatements();
    }

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

    public Transaction getTransaction() {
        return transaction;
    }

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


}

1.9 第九步:完善SqlSessionFactoryBuilder中的build方法

这里我们定义一个常量类,用于方便后续的读取:,提高代码的可读性。

在这里插入图片描述

package org.god.ibatis.core;

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";

}

在这里插入图片描述

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 构建器对象
 * 通过SqlSessionFactoryBuilder的 build 方法来解析
 * godbatis-config.xml文件,然后创建sqlSessionFactory对象
 */

public class SqlSessionFactoryBuilder {

    /**
     *
     */
    public SqlSessionFactoryBuilder() {
    }


    /**
     * 解析 godbatis-config.xml 文件,来构建sqlSessionFactory对象
     *
     * @param in 指向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<>();
            List<Node> nodes = document.selectNodes("//mapper"); // 获取整个配置文件中所有的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
            Map<String, MappedStatement> mappedStatements = getMappedStatements(sqlMapperXMLPathList);


            // 解析完成之后,构建SqlSessionFactory对象
            factory = new SqlSessionFactory(transaction, mappedStatements);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;

    }

    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");
                    System.out.println(resultType);
                    String sql = element.getTextTrim();
                    System.out.println(sql);

                    MappedStatement mappedStatement = new MappedStatement(sql, resultType);
                    mappedStatements.put(sqlId,mappedStatement);
                });
            } catch (DocumentException e) {
                e.printStackTrace();
            }
        });

        return mappedStatements;
    }

    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;
    }


    /**
     * 获取数据源对象
     *
     * @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;

        //UNPOOLED POOLED JNDI
        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;
    }


}

1.10 第十步:编写SqlSession类中commit rollback close方法

在这里插入图片描述

package org.god.ibatis.core;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

public class SqlSession {

    private SqlSessionFactory factory;

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

    /**
     * 提交事务
     */
    public void commit() {
        factory.getTransaction().commit();
    }


    /**
     * 回滚事务
     */
    public void rollback() {
        factory.getTransaction().rollback();
    }


    /**
     * 关闭事务
     */
    public void close() {
        factory.getTransaction().close();
    }
}

这里我们手写MyBatis 就仅仅只实现 insert() 和 selectOne(单条记录的查询)操作,并且有些局限就是,数据库的所有字段类型,都必须是 varchar 类型才行。

1.11 第十一步:编写SqlSession类中的insert方法

在这里插入图片描述

package org.god.ibatis.core;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

public class SqlSession {

    private SqlSessionFactory factory;

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


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

        try {
            // JDBC代码,执行insert 语句 ,完成插入操作
            Connection connection = factory.getTransaction().getConnection();
            // insert into t_car values(#{id},#{name},#{age})
            String godbatisSql = factory.getMappedStatements().get(sqlId).getSql();
            System.out.println(factory.getMappedStatements().get(sqlId));
            System.out.println(factory.getMappedStatements().get(sqlId).getSql());
            // insert into t_user values(?,?,?)
            String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$]*}", "?");
            PreparedStatement ps = connection.prepareStatement(sql);
            // 给 ? 占位符传值
            // 难度是什么?
            // 第一: 你不知道有多少个?
            // 第二:你不知道该将Pojo对象中的那个属性赋值给哪个?
            // ps.String(第几个问号,传什么值); // 这里都是setString,所以数据库中的字段类型要求都是varchar才行,
            // 这是godbatis 比较失败的地方
            int formIndex = 0;
            int index = 1;

            while (true) {
                int jingIndex = godbatisSql.indexOf("#", formIndex);
                if (jingIndex < 0) {
                    break;
                }

                System.out.println(index);

                int youKuoHaoIndex = godbatisSql.indexOf("}", formIndex);
                String propertyName = godbatisSql.substring(jingIndex + 2, youKuoHaoIndex).trim();
                System.out.println(propertyName);
                formIndex = 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++;

            }
            count = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 给?占位符传值
        return count;
    }


    /**
     * 查询一个对象
     * @param sqlId
     * @param parameterObj
     * @return
     */
    public Object selectOne(String sqlId, Object parameterObj){
        MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId);
        Connection connection = factory.getTransaction().getConnection();
        // 获取sql语句
        String godbatisSql = mappedStatement.getSql();
        String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
        // 执行sql
        PreparedStatement ps = null;
        ResultSet rs = null;
        Object obj = null;
        try {
            ps = connection.prepareStatement(sql);
            ps.setString(1, parameterObj.toString());
            rs = ps.executeQuery();
            if (rs.next()) {
                // 将结果集封装对象,通过反射
                String resultType = mappedStatement.getResultType();
                Class<?> aClass = Class.forName(resultType);
                obj = aClass.newInstance();
                // 给对象obj属性赋值
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = rsmd.getColumnName(i);
                    String setMethodName = "set" + columnName.toUpperCase().charAt(0) + columnName.substring(1);
                    Method setMethod = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());
                    setMethod.invoke(obj, rs.getString(columnName));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            try {
                ps.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return obj;
    }




    /**
     * 提交事务
     */
    public void commit() {
        factory.getTransaction().commit();
    }


    /**
     * 回滚事务
     */
    public void rollback() {
        factory.getTransaction().rollback();
    }


    /**
     * 关闭事务
     */
    public void close() {
        factory.getTransaction().close();
    }
}

1.12 第十二步:编写SqlSession类中的selectOne方法

在这里插入图片描述

package org.god.ibatis.core;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

public class SqlSession {

    private SqlSessionFactory factory;

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


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

        try {
            // JDBC代码,执行insert 语句 ,完成插入操作
            Connection connection = factory.getTransaction().getConnection();
            // insert into t_car values(#{id},#{name},#{age})
            String godbatisSql = factory.getMappedStatements().get(sqlId).getSql();
            System.out.println(factory.getMappedStatements().get(sqlId));
            System.out.println(factory.getMappedStatements().get(sqlId).getSql());
            // insert into t_user values(?,?,?)
            String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_$]*}", "?");
            PreparedStatement ps = connection.prepareStatement(sql);
            // 给 ? 占位符传值
            // 难度是什么?
            // 第一: 你不知道有多少个?
            // 第二:你不知道该将Pojo对象中的那个属性赋值给哪个?
            // ps.String(第几个问号,传什么值); // 这里都是setString,所以数据库中的字段类型要求都是varchar才行,
            // 这是godbatis 比较失败的地方
            int formIndex = 0;
            int index = 1;

            while (true) {
                int jingIndex = godbatisSql.indexOf("#", formIndex);
                if (jingIndex < 0) {
                    break;
                }

                System.out.println(index);

                int youKuoHaoIndex = godbatisSql.indexOf("}", formIndex);
                String propertyName = godbatisSql.substring(jingIndex + 2, youKuoHaoIndex).trim();
                System.out.println(propertyName);
                formIndex = 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++;

            }
            count = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 给?占位符传值
        return count;
    }


    /**
     * 查询一个对象
     * @param sqlId
     * @param parameterObj
     * @return
     */
    public Object selectOne(String sqlId, Object parameterObj){
        MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId);
        Connection connection = factory.getTransaction().getConnection();
        // 获取sql语句
        String godbatisSql = mappedStatement.getSql();
        String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
        // 执行sql
        PreparedStatement ps = null;
        ResultSet rs = null;
        Object obj = null;
        try {
            ps = connection.prepareStatement(sql);
            ps.setString(1, parameterObj.toString());
            rs = ps.executeQuery();
            if (rs.next()) {
                // 将结果集封装对象,通过反射
                String resultType = mappedStatement.getResultType();
                Class<?> aClass = Class.forName(resultType);
                obj = aClass.newInstance();
                // 给对象obj属性赋值
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = rsmd.getColumnName(i);
                    String setMethodName = "set" + columnName.toUpperCase().charAt(0) + columnName.substring(1);
                    Method setMethod = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());
                    setMethod.invoke(obj, rs.getString(columnName));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            try {
                ps.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return obj;
    }

    /**
     * 执行查询语句,返回一个对象,该方法只适合返回一条记录的sql语句
     * @param sqlId
     * @param param
     * @return
     */
    public Object selectOne2(String sqlId,Object param) {
        Object obj = null;
        try {
            Connection connection = factory.getTransaction().getConnection();
            MappedStatement mappedStatement = factory.getMappedStatements().get(sqlId);

            // 这是哪个DQL查询语句
            // select id,name,age 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();
            // 从结果集中取数据,封装Java对象

            if(rs.next()) {
                // 获取 resultType 的 Class对象
                Class<?> resultTypeClass = Class.forName(resultType);
                // 调用无参数构造方法创建对象
                obj = resultTypeClass.newInstance();  // Object obj = new User();
                // 给User类的id,name,age 属性赋值
                // 给Obj 对象的哪个属性赋哪个值

                /*
                解决问题的关键:将查询结果的列名作为属性名
                列名是id,那么属性就是:id
                列名是name,那么属性名就是:name
                 */
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();

                for (int i = 0;i < columnCount; i++) {
                    String propertyName = rsmd.getColumnName(i+1);
                    // 拼接方法名:
                   String setMethodName =  "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
                   // 获取set 方法
                   // Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName,resultTypeClass.getDeclaredField(propertyName).getType());
                    Method setMethod = resultTypeClass.getDeclaredMethod(setMethodName,String.class);
                    // 调用set 方法给对象Obj属性赋值
                    setMethod.invoke(obj,rs.getString(propertyName));
                }
            }


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















    /**
     * 提交事务
     */
    public void commit() {
        factory.getTransaction().commit();
    }


    /**
     * 回滚事务
     */
    public void rollback() {
        factory.getTransaction().rollback();
    }


    /**
     * 关闭事务
     */
    public void close() {
        factory.getTransaction().close();
    }
}

2. 将我们自己手写的MyBatiks 名为“godbatis”的框架,使用Maven打包

在这里插入图片描述

在这里插入图片描述

根据所提示的放置的路径的位置,查看本地仓库中是否已经有jar包:

在这里插入图片描述

3. 使用我们自己手写的 MyBatis 名为 “godbatis” 的框架,运行测试

使用godbatis 就和使用MyBatis是一样的。

第一步:准备数据库表t_user。这里我们的手写的MyBatis 框架有所局限,缺陷,只能识别字段类型为 varchar 的数据,所以我们这里的数据表的,字段的类型都定义为了 varchar 类型的。

在这里插入图片描述

表数据:

在这里插入图片描述

第二步:创建模块,普通的Java Maven模块:godbatis-test

第三步:引入依赖

在这里插入图片描述

<?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>com.rainbowsea</groupId>
    <artifactId>godbatis-test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.god.ibatis</groupId>
            <artifactId>godbatis</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

第四步:编写pojo类

在这里插入图片描述

package com.rainbowsea.godbatis.pojo;

public class User {
    private String id;
    private String name;
    private String age;


    public User(String id, String name, String age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

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

    public User() {
    }

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

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

第五步:编写核心配置文件:godbatis-config.xml

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <environments default="mybatis">
        <environment id="mybatis">
            <!--            MANAGED 没有用第三框架管理的话,都是会被提交的,没有事务上的管理了。-->
            <transactionManager type="JDBC"/>
<!--            数据源是获取connection对象的 -->
<!--            POOlED UNPOOLED  JNDI -->
<!--            所有的数据源都要实现 JDK带的规范,javax.sql.DataSource-->
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="sqlMapper.xml"></mapper>
    </mappers>
</configuration>

第六步:编写sql映射文件:sqlMapper.xml

在这里插入图片描述

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


<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="user">

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <insert id="insert">
        insert into t_user values(#{id},#{name},#{age})
    </insert>

    <select id="selectAll" resultType="org.god.ibatis.pojo.User">
        select id,name,age from t_user where id = #{id}
    </select>
</mapper>

第七步:编写测试类

在这里插入图片描述

package com.rainbowsea.godbatis.test;

import org.god.ibatis.core.SqlSession;
import org.god.ibatis.core.SqlSessionFactory;
import org.god.ibatis.core.SqlSessionFactoryBuilder;
import org.god.ibatis.pojo.User;
import org.god.ibatis.utils.Resources;
import org.junit.Test;

public class UserMapperTest {

    @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("99999", "张三", "20");
        int count = sqlSession.insert("user.insert", user);
        sqlSession.commit();
        sqlSession.close();

    }

    @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.selectOne2("user.selectAll", "666");
        System.out.println(obj);
        sqlSession.close();
    }
}

在这里插入图片描述

在这里插入图片描述

4. 总结:

  1. 大部分的框架的实现都是:反射机制 + 设计模式 + 注解 的方式实现的。
  2. 这里我们手写MyBatis 就仅仅只实现 insert() 和 selectOne(单条记录的查询)操作,并且有些局限就是,数据库的所有字段类型,都必须是 varchar 类型才行。
  3. 手写MyBatis 让我们更好的了解了 MyBatis 的运行机理,是如何通过反射机制读取相关信息进行设置的。

5. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1829525.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国产MCU芯片(2):东软MCU概览

前言: 国产芯片替代的一个主战场之一就是mcu,可以说很多国内芯片设计公司都打算或者已经在设计甚至有了一款或多款的量产产品了,这也是国际大背景决定的。过去的家电市场、过去的汽车电子市场,的确国产芯片的身影不是很常见,如今不同了,很多fabless投身这个行业,一种是…

开源语音合成模型ChatTTS本地部署结合内网穿透实现远程访问

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章就教大家如何快速地在Windows中本地部署ChatTTS&#xff0c;并且我们还可以结合Cpolar内网穿透实现公网随时随地访问ChatTTS AI语言模型。 最像人声的AI来了&a…

跟着刘二大人学pytorch(第---12---节课之RNN基础篇)

文章目录 0 前言0.1 课程视频链接&#xff1a;0.2 课件下载地址&#xff1a; 1 Basic RNN1.1 复习DNN和CNN1.2 直观认识RNN1.3 RNN Cell的内部计算方式 2 具体什么是一个RNN&#xff1f;3 使用pytorch构造一个RNN3.1 手动构造一个RNN Cell来实现RNN3.2 直接使用torch中现有的RN…

(4)SDL渲染开发

SDL渲染开发 1. SDL简介2. 环境搭建2.1 windows2.2 Linux 3. SDL子系统3.1 eg1 创建窗口 4&#xff1a;SDL显示4.1 显示图片4.2 绘制长方形显示 5. SDL事件6. SDL多线程6.1 接口演示6.2 yuv显示6.3 pcm播放 1. SDL简介 SDL&#xff08;Simple DirectMedia Layer&#xff09;是一…

数据库概述1

数据&#xff1a;描述事物的符号记录称为数据&#xff1b; 包括数字、图片、音频等&#xff1b; 数据库&#xff1a;长期储存在计算机内有组织、可共享的大量数据的集合&#xff1b;数据库中的数据按照一定的数据模型组织、描述和存储&#xff0c;具有较小的数据冗余、较高的数…

【支持向量机】问题梳理

学完支持向量机后我有些地方不太清楚&#xff0c;故做如下梳理&#xff1a; 1.为什么支持向量机模型认为一个点划分正确的标志是y(wxb)>1呢&#xff0c;为什么不是y(wxb)>0&#xff0c;比如y为1&#xff0c;wxb为0.5&#xff0c;大于0&#xff0c;则预测正确。 2.所以意思…

[Python]Anaconda相关命令

环境操作相关命令 查看所有环境 conda env list创建环境 conda create --name cahttts python3.10激活环境 conda activate cahttts安装依赖文件 pip install -r requirements.txt查看GPU型号 nvidia-smi -LGPU 0: NVIDIA A10 (UUID: GPU-9f1fc9cf-582a-25ac-849c-2f77343…

FFmpeg编解码的那些事(3)-视频硬解码的基础知识

目录 前言&#xff1a; 1.iso/os x平台 2.windows平台 3.linux平台 4.Tips&#xff1a; 5.结论&#xff1a; 前言&#xff1a; 视频硬解码的过程就是把视频提取成图片变显示出来&#xff0c;就是播放器播放视频的过程&#xff0c;就可以理解为解码的过程。 在不同的系统…

R进阶使用技巧

Introduction 分享一些R进阶使用的技巧&#xff0c;相当于是之前写的R语言学习的实践和总结了。 Online slide: https://asa-blog.netlify.app/R_tips_for_advanced_use_byAsa/R_tips.html 下载slide和相关的各种test文件: https://asa-blog.netlify.app/R_tips_for_advanced…

【论文阅读】AttnDreamBooth | 面向文本对齐的个性化图片生成

文章目录 1 动机2 方法3 实验 1 动机 使用灵活的文本控制可以实现一些特定的概念的注入从而实现个性化的图片生成。 最经典的比如一些好玩的动漫人物的概念&#xff0c;SD大模型本身是不知道这些概念的&#xff0c;但是通过概念注入是可以实现的从而生成对应的动漫人物 两个…

element-plus表单组件之自动补全组件el-autocomplete和级联选择器组件el-cascader

el-autocomplete 自动补全组件 自补全组件的功能和可以根据输入过滤的el-select组件有些类似。 fetch-suggestions 根据输入框的输入获取建议的内容&#xff0c;其接受值是一个函数&#xff0c;有2个参数&#xff0c;querystring:输入的内容&#xff0c;callback内置函数&…

爱心代码来喽

今天给大家分享一个爱心代码&#xff0c;送给我的粉丝们。愿你们天天开心&#xff0c;事事顺利&#xff0c;学业和事业有成。 下面是运行代码&#xff1a; #include<stdio.h> #include<Windows.h> int main() { system(" color 0c"); printf(&q…

代码随想录-Day32

122. 买卖股票的最佳时机 II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能…

【算法专题--链表】删除排序链表中的重复元素II -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 双指针 -- 采用 哨兵位头节点 &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 &#x1f34d; 案例图解 四、总结与提炼 五、共勉 一、前言 删除排序链表中的重复元素II元素这道题&#xff0c…

MySQL之优化服务器设置(五)

优化服务器设置 高级InnoDB设置 innodb_old_blocks_time InnoDB有两段缓冲池LRU(最近最少使用)链表&#xff0c;设计目的是防止换出长期很多次的页面。像mysqldump产生的这种一次性的(大)查询&#xff0c;通常会读取页面到缓冲池的LRU列表&#xff0c;从中读取需要的行&…

ubuntu中安装docker并换源

使用 Ubuntu 的仓库安装 Docker sudo apt update现在&#xff0c;你可以使用以下命令在 Ubuntu 中安装 Docker 以及 Docker Compose&#xff1a; sudo apt install docker.io docker-composeDocker 包被命名为 docker.io&#xff0c;因为在 Docker 出现之前就已经存在一个名为…

C++ virtual public(虚继承类)

这个"virtual"有什么作用&#xff1f; 由于C支持多重继承&#xff0c;所以对于一个派生类中有几个直接父类&#xff0c;而几个直接父类中有几个可能分别继承自某一个基类&#xff08;就是父类的父类&#xff09;&#xff0c;这样在构造最终派生类时&#xff0c;会出现…

Nginx + Tomcat 负载均衡、动静分离

前言 Tomcat简介 最初是由Sun的软件构架师詹姆斯邓肯戴维森开发 安装Tomcat后&#xff0c;安装路径下面的目录和文件&#xff0c;是使用或者配置Tomcat的重要文件 Nginx 应用 Nginx是一款非常优秀的HTTP服务器软件 &#xff08;1&#xff09;支持高达50 000个并发连接数的响应…

单目标应用:基于三角拓扑聚合优化算法TTAO的微电网优化(MATLAB代码)

一、微电网模型介绍 微电网多目标优化调度模型简介_vmgpqv-CSDN博客 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、三角拓扑聚合优化算法求解微电网 2.1算法简介 三角拓扑聚合优化算法&…

毕业了校园卡怎么改套餐?

毕业了校园卡怎么改套餐&#xff1f; 毕业生校园卡99元套餐变更8元保号套餐教程 学弟学妹们恭喜毕业呀&#x1f393; 校园卡绑定了好多东西注销不掉又不想交高额月租的看过来。 今天一招教你更改校园卡套餐。 中国移动/电信/联通App 打开App&#xff0c;在首页右上角点击人工…