自定义Mybatis框架

2023-10-27

通过快速入门示例(Mybatis快速入门),我们发现使用 mybatis 是非常容易的一件事情,因为只需要编写 Dao 接口并且按照mybatis 要求编写两个配置文件,就可以实现功能。远比我们之前的 jdbc 方便多了。(我们使用注解之后,将变得更为简单,只需要编写一个 mybatis 配置文件就够了。)

但是,这里面包含了许多细节,比如为什么会有工厂对象(SqlSessionFactory),为什么有了工厂之后还要有构建者对象(SqlSessionFactoryBuilder),为什么 IUserDao.xml 在创建时有位置和文件名的要求等等。

这些问题在自定义 mybatis 框架的过程中,通过层层剥离的方式呈现出来。
尝试自定义 Mybatis 框架,不是自己去写个 mybatis,而是为了更好的了解mybatis 内部是怎么执行的,在以后的开发中能更好的使用 mybatis 框架,同时对它的设计理念(设计模式)有一个认识。
-------> 项目源码

自定义Mybatis分析

在这里插入图片描述
Mybatis在使用代理dao的方式实现增删改查时做了两件事:

  • 创建代理对象
  • 在代理对象中调用selectList方法

selectList方法能够通过解析配置文件,提取必要信息来实现:
在这里插入图片描述
要想让此方法执行,还需要给其提供两个信息:

  • 连接信息
  • 映射信息

映射信息包含两部分:

  • SQL语句
  • 用来封装结果的实体类的全限定类名

我们可以定义一个Mapper对象来组合上面两个信息,将Mapper作为值,“类.方法”作为键,这样一个键值对存入map来维护。

轮子缺少的配件

分析完后,抛弃mybatis的依赖,看看我们需要造的小轮子还差什么部件,然后逐一构造:
在这里插入图片描述
可以看到缺少了这些:
class Resoures
class SqlSessionFactoryBuilder
interface SqlSessionFactory
interface SqlSession

组装轮子

制定骨架

Resoures:
加载资源比较简单

/**
 * 使用类加载器读取配置文件的类
 */
public class Resources {

    /**
     * 根据传入的参数,获取一个字节输入流
     * @param filePath
     * @return
     */
    public static InputStream getResourceAsStream(String filePath){
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}

SqlSessionFactoryBuilder:

/**
 * 用于创建一个SqlSessionFactory对象
 */
public class SqlSessionFactoryBuilder {

    /**
     * 根据参数的字节输入流来构建一个SqlSessionFactory工厂
     *
     * @param config
     * @return
     */
    public SqlSessionFactory build(InputStream config) {
        Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
        return new DefaultSqlSessionFactory(cfg);
    }
}

build返回值为SqlSessionFactory:

public interface SqlSessionFactory {

    /**
     * 用于打开一个新的SqlSession对象
     * @return
     */
    SqlSession openSession();
}

紧接着,定义SqlSession对象:

/**
 * 自定义Mybatis中和数据库交互的核心类
 *  它里面可以创建dao接口的代理对象
 */
public interface SqlSession {

    /**
     * 根据参数创建一个代理对象
     * @param daoInterfaceClass dao的接口字节码
     * @param <T>
     * @return
     */
    <T> T getMapper(Class<T> daoInterfaceClass);

    /**
     * 释放资源
     */
    void close();
}

以上定好了框架,开始填充细节。

解析配置文件

SqlSessionFactoryBuilder的build方法,就需要读取配置文件,这里使用工具类:
XMLConfigBuilder:

/**
 * 用于解析配置文件
 */
public class XMLConfigBuilder {


    /**
     * 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方
     * 使用的技术:
     * dom4j+xpath
     */
    public static Configuration loadConfiguration(InputStream config) {
        try {
            //定义封装连接信息的配置对象(mybatis的配置对象)
            Configuration cfg = new Configuration();

            //1.获取SAXReader对象
            SAXReader reader = new SAXReader();
            //2.根据字节输入流获取Document对象
            Document document = reader.read(config);
            //3.获取根节点
            Element root = document.getRootElement();
            //4.使用xpath中选择指定节点的方式,获取所有property节点
            List<Element> propertyElements = root.selectNodes("//property");
            //5.遍历节点
            for (Element propertyElement : propertyElements) {
                //判断节点是连接数据库的哪部分信息
                //取出name属性的值
                String name = propertyElement.attributeValue("name");
                if ("driver".equals(name)) {
                    //表示驱动
                    //获取property标签value属性的值
                    String driver = propertyElement.attributeValue("value");
                    cfg.setDriver(driver);
                }
                if ("url".equals(name)) {
                    //表示连接字符串
                    //获取property标签value属性的值
                    String url = propertyElement.attributeValue("value");
                    cfg.setUrl(url);
                }
                if ("username".equals(name)) {
                    //表示用户名
                    //获取property标签value属性的值
                    String username = propertyElement.attributeValue("value");
                    cfg.setUsername(username);
                }
                if ("password".equals(name)) {
                    //表示密码
                    //获取property标签value属性的值
                    String password = propertyElement.attributeValue("value");
                    cfg.setPassword(password);
                }
            }
            //取出mappers中的所有mapper标签,判断他们使用了resource还是class属性
            List<Element> mapperElements = root.selectNodes("//mappers/mapper");
            //遍历集合
            for (Element mapperElement : mapperElements) {
                //判断mapperElement使用的是哪个属性
                Attribute attribute = mapperElement.attribute("resource");
                if (attribute != null) {
                    System.out.println("使用的是XML");
                    //表示有resource属性,用的是XML
                    //取出属性的值
                    String mapperPath = attribute.getValue();//获取属性的值"com/itheima/dao/IUserDao.xml"
                    //把映射配置文件的内容获取出来,封装成一个map
                    Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
                    //给configuration中的mappers赋值
                    cfg.setMappers(mappers);
                } else {
                    System.out.println("使用的是注解");
                    //表示没有resource属性,用的是注解
                    //获取class属性的值
                    String daoClassPath = mapperElement.attributeValue("class");
                    //根据daoClassPath获取封装的必要信息
                    Map<String, Mapper> mappers = loadMapperAnnotation(daoClassPath);
                    //给configuration中的mappers赋值
                    cfg.setMappers(mappers);
                }
            }
            //返回Configuration
            return cfg;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                config.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

  /**
     * 根据传入的参数,解析XML,并且封装到Map中
     *
     * @param mapperPath 映射配置文件的位置
     * @return map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
     * 以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
     */
    private static Map<String, Mapper> loadMapperConfiguration(String mapperPath) throws IOException {
        InputStream in = null;
        try {
            //定义返回值对象
            Map<String, Mapper> mappers = new HashMap<String, Mapper>();
            //1.根据路径获取字节输入流
            in = Resources.getResourceAsStream(mapperPath);
            //2.根据字节输入流获取Document对象
            SAXReader reader = new SAXReader();
            Document document = reader.read(in);
            //3.获取根节点
            Element root = document.getRootElement();
            //4.获取根节点的namespace属性取值
            String namespace = root.attributeValue("namespace");//是组成map中key的部分
            //5.获取所有的select节点
            List<Element> selectElements = root.selectNodes("//select");
            //6.遍历select节点集合
            for (Element selectElement : selectElements) {
                //取出id属性的值      组成map中key的部分
                String id = selectElement.attributeValue("id");
                //取出resultType属性的值  组成map中value的部分
                String resultType = selectElement.attributeValue("resultType");
                //取出文本内容            组成map中value的部分
                String queryString = selectElement.getText();
                //创建Key
                String key = namespace + "." + id;
                //创建Value
                Mapper mapper = new Mapper();
                mapper.setQueryString(queryString);
                mapper.setResultType(resultType);
                //把key和value存入mappers中
                mappers.put(key, mapper);
            }
            return mappers;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            in.close();
        }
    }
   }

读取到的信息封装在Configuration:

/**
 * 自定义mybatis的配置类,解析配置文件后的结果存于此
 */
public class Configuration {

    private String driver;
    private String url;
    private String username;
    private String password;

    private Map<String,Mapper> mappers = new HashMap<String,Mapper>();

    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
        this.mappers.putAll(mappers);//此处需要使用追加的方式
    }
	// 其他getter setter省略
}

映射信息封装对象Mapper:

/**
 * 用于封装执行的SQL语句和结果类型的全限定类名
 */
public class Mapper {

    private String queryString;//SQL
    private String resultType;//实体类的全限定类名

    public String getQueryString() {
        return queryString;
    }

    public void setQueryString(String queryString) {
        this.queryString = queryString;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }
}

类关系梳理,创建默认实现类

再次梳理各个类之间的关系:Resources类读取配置文件的流,交给构建者,构建者使用工具类(解析xml)构建出工厂对象,工厂可以生产SqlSession对象(通过将配置对象传入openSession方法)。在这个对象中,我们需要完成创建代理对象和查询所有的操作。

DefaultSqlSessionFactory:

/**
 * SqlSessionFactory接口的实现类
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private Configuration cfg;

    public DefaultSqlSessionFactory(Configuration cfg){
        this.cfg = cfg;
    }

    /**
     * 用于创建一个新的操作数据库对象
     * @return
     */
    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(cfg);
    }
}

DefaultSqlSession:

/**
 * SqlSession接口的实现类
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration cfg;
    private Connection connection;

    public DefaultSqlSession(Configuration cfg){
        this.cfg = cfg;
        connection = DataSourceUtil.getConnection(cfg);
    }

    /**
     * 用于创建代理对象
     * @param daoInterfaceClass dao的接口字节码
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<T> daoInterfaceClass) {
        return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
                new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection));
    }

    /**
     * 用于释放资源
     */
    @Override
    public void close() {
        if(connection != null) {
            try {
                connection.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

其中的连接是通过工具类获得的,我们再写一下这个工具类:

/**
 * 用于创建数据源的工具类
 */
public class DataSourceUtil {

    /**
     * 用于获取一个连接
     * @param cfg
     * @return
     */
    public static Connection getConnection(Configuration cfg){
        try {
            Class.forName(cfg.getDriver());
            return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

SqlSession创建代理对象的getMapper方法中,newProxyInstance需要传一个核心参数,就是
代理类MapperProxy,定义代理的具体方式,也就是在这个实现类中调用selectList方法:

/**
 * 代理对象完成对方法的增强
 */
public class MapperProxy implements InvocationHandler {

    // map的key是全限定类名+方法名
    private Map<String, Mapper> mappers;
    private Connection conn;

    public MapperProxy(Map<String,Mapper> mappers,Connection conn){
        this.mappers = mappers;
        this.conn = conn;
    }

    /**
     * 用于对方法进行增强的,我们的增强其实就是调用selectList方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.获取方法名
        String methodName = method.getName();
        //2.获取方法所在类的名称
        String className = method.getDeclaringClass().getName();
        //3.组合key
        String key = className+"."+methodName;
        //4.获取mappers中的Mapper对象
        Mapper mapper = mappers.get(key);
        //5.判断是否有mapper
        if(mapper == null){
            throw new IllegalArgumentException("传入的参数有误");
        }
        //6.调用工具类执行查询所有
        return new Executor().selectList(mapper,conn);
    }
}

最后查询所有通过工具类来完成:

/**
 * 负责执行SQL语句,并且封装结果集
 */
public class Executor {

    public <E> List<E> selectList(Mapper mapper, Connection conn) {
        PreparedStatement pstm = null;
        ResultSet rs = null;
        try {
            //1.取出mapper中的数据
            String queryString = mapper.getQueryString();//select * from user
            String resultType = mapper.getResultType();//com.itheima.domain.User
            Class domainClass = Class.forName(resultType);
            //2.获取PreparedStatement对象
            pstm = conn.prepareStatement(queryString);
            //3.执行SQL语句,获取结果集
            rs = pstm.executeQuery();
            //4.封装结果集
            List<E> list = new ArrayList<E>();//定义返回值
            while(rs.next()) {
                //实例化要封装的实体类对象
                E obj = (E)domainClass.newInstance();

                //取出结果集的元信息:ResultSetMetaData
                ResultSetMetaData rsmd = rs.getMetaData();
                //取出总列数
                int columnCount = rsmd.getColumnCount();
                //遍历总列数
                for (int i = 1; i <= columnCount; i++) {
                    //获取每列的名称,列名的序号是从1开始的
                    String columnName = rsmd.getColumnName(i);
                    //根据得到列名,获取每列的值
                    Object columnValue = rs.getObject(columnName);
                    //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
                    PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
                    //获取它的写入方法
                    Method writeMethod = pd.getWriteMethod();
                    //把获取的列的值,给对象赋值
                    writeMethod.invoke(obj,columnValue);
                }
                //把赋好值的对象加入到集合中
                list.add(obj);
            }
            return list;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            release(pstm,rs);
        }
    }


    private void release(PreparedStatement pstm,ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

        if(pstm != null){
            try {
                pstm.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

实现基于注解的查询

显然需要我们自己定义注解:

/**
 * 查询的注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {

    /**
     * 配置SQL语句的
     * @return
     */
    String value();
}

在负责解析的工具类中需要填加解析注解的部分:

/**
     * 根据传入的参数,得到dao中所有被select注解标注的方法。
     * 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
     *
     * @param daoClassPath
     * @return
     */
    private static Map<String, Mapper> loadMapperAnnotation(String daoClassPath) throws Exception {
        //定义返回值对象
        Map<String, Mapper> mappers = new HashMap<String, Mapper>();

        //1.得到dao接口的字节码对象
        Class daoClass = Class.forName(daoClassPath);
        //2.得到dao接口中的方法数组
        Method[] methods = daoClass.getMethods();
        //3.遍历Method数组
        for (Method method : methods) {
            //取出每一个方法,判断是否有select注解
            boolean isAnnotated = method.isAnnotationPresent(Select.class);
            if (isAnnotated) {
                //创建Mapper对象
                Mapper mapper = new Mapper();
                //取出注解的value属性值
                Select selectAnno = method.getAnnotation(Select.class);
                String queryString = selectAnno.value();
                mapper.setQueryString(queryString);
                //获取当前方法的返回值,还要求必须带有泛型信息
                Type type = method.getGenericReturnType();//List<User>
                //判断type是不是参数化的类型
                if (type instanceof ParameterizedType) {
                    //强转
                    ParameterizedType ptype = (ParameterizedType) type;
                    //得到参数化类型中的实际类型参数
                    Type[] types = ptype.getActualTypeArguments();
                    //取出第一个
                    Class domainClass = (Class) types[0];
                    //获取domainClass的类名
                    String resultType = domainClass.getName();
                    //给Mapper赋值
                    mapper.setResultType(resultType);
                }
                //组装key的信息
                //获取方法的名称
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String key = className + "." + methodName;
                //给map赋值
                mappers.put(key, mapper);
            }
        }
        return mappers;
    }

目录结构

在这里插入图片描述

流程图

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义Mybatis框架 的相关文章

  • Jackson JSON + Java 泛型

    我正在尝试将以下 JSON 反序列化 映射到List
  • 使用 proguard 混淆文件名

    我正在使用 proguard 和 Android Studio 混淆我的 apk 当我反编译我的apk时 我可以看到很多文件 例如aaa java aab java ETC 但我项目中的所有文件都有原始名称 有没有办法混淆我的项目的文件名
  • 在不支持 CAS 操作的处理器上进行 CompareAndSet

    今天 我在一次采访中被问到下一个问题 如果您在具有不支持 CAS 操作的处理器的机器上调用 AtomicLong 的compareAndSet 方法 会发生什么情况 您能否帮我解决这个问题 并在可能的情况下提供一些全面描述的链接 From
  • 无法使用 datastax java 驱动程序通过 UDT 密钥从 cassandra 检索

    我正在尝试使用用户定义的类型作为分区键将对象存储在 cassandra 中 我正在使用 datastax java 驱动程序进行对象映射 虽然我能够插入到数据库中 但无法检索该对象 如果我更改分区键以使用非 udt 例如文本 我就能够保存和
  • 未装饰窗户的 Windows Snap 功能?

    有谁知道如何允许未装饰的窗户使用此功能 唯一的选择就是重新实施它 有任何想法吗 谢谢 可停靠可能是唯一的JToolBar http docs oracle com javase tutorial uiswing components too
  • getCurrentSession 在网络中休眠

    我正在使用 hibernate 和 jsp servlet 编写一个基于 Web 的应用程序 我读过有关sessionFactory getCurrentSession and sessionFactory openSession方法 我知
  • JAXB - 忽略元素

    有什么方法可以忽略 Jaxb 解析中的元素吗 我有一个很大的 XML 文件 如果我可以忽略其中一个大而复杂的元素 那么它的解析速度可能会快很多 如果它根本无法验证元素内容并解析文档的其余部分 即使该元素不正确 那就更好了 例如 这应该只生成
  • 如何更改 Swagger-ui URL 前缀?

    我正在使用 Springfox Swagger2 和 Spring boot 1 5 9 我可以通过此链接访问 swagger UI http localhost 8090 swagger ui html http localhost 80
  • 使用 JDBC 连接到 PostgreSql 的本地实例

    我在 Linux 机器上有一个正在运行的 PostgreSql 本地实例 当我使用psql来自 shell 的命令我成功登录 没有任何问题 我需要通过 JDBC 连接到 PostgreSql 但我不知道我到底应该传递什么url参数为Driv
  • 为什么解析这个 JSON 会抛出错误?

    我正在尝试解析这个 JSONObject query yahoo count 1 results rate Name USD INR id USDINR Time 12 19pm Date 10 31 2015 Bid 65 405 Ask
  • Joshua Bloch 的构建器设计模式有何改进?

    早在 2007 年 我就读过一篇关于 Joshua Blochs 所采用的 构建器模式 的文章 以及如何修改它以改善构造函数和 setter 的过度使用 特别是当对象具有大量属性 其中大部分属性是可选的 时 本文对此设计模式进行了简要总结
  • Java 8 Stream,获取头部和尾部

    Java 8 引入了Stream http download java net jdk8 docs api java util stream Stream html类似于 Scala 的类Stream http www scala lang
  • 按降序排序映射java8 [重复]

    这个问题在这里已经有答案了 private static
  • Lombok 不适用于 Eclipse Neon

    我下载了lombok jar lombok 1 16 14 jar 并将其放入我的下载中 然后我点击这个 jar 执行正确地识别了我的 MacOS 上的 Eclipse 实例 然后我选择了我想要的实例 Lombok也在pom xml中指定
  • Azure Java SDK:ServiceException:ForbiddenError:

    尝试了基本位置检索器代码 如下所示 String uri https management core windows net String subscriptionId XXXXXXXX 5fad XXXXXX 9dfa XXXXXX St
  • 用于请求带有临时缓存的远程 Observable 的 RxJava 模式

    用例是这样的 我想暂时缓存最新发出的昂贵的Observable响应 但在它过期后 返回到昂贵的源Observable并再次缓存它 等等 一个非常基本的网络缓存场景 但我真的很难让它工作 private Observable
  • Spring Data Rest 多对多 POST

    首先 让我解释一下我的用例 这非常简单 有一个用户实体和一个服务实体 我使用 UserService 作为连接实体 连接表 在用户和服务之间建立多对多关联最初 会有一些用户集和一些服务集 用户可以在任何时间点订阅任何服务 在这种情况下 将向
  • 使用 Apache 允许 Glassfish 和 PHP 在同一服务器中协同工作

    是否可以建立从 Java 到 php 文件的桥梁 我有一个用 Java 编写的应用程序 我需要执行http piwik org http piwik org 这是用 PHP 编写的 在服务器中 我正在运行 PHP 但无法从浏览器访问 php
  • 检测到 JVM 正在关闭

    我有一个使用 addShutdownHook 处理 Ctrl C 的 Swing 应用程序 它工作正常 直到我的关闭任务之一调用一个在正常情况下更改 JLabel 文本的函数 此时它挂起 我认为问题是 Swing EDT 已终止或正在等待某
  • GAE 无法部署到 App Engine

    我正在尝试从 Eclipse 发布 Web 应用程序 我在 GAE 上创建了四个项目 可以通过登录我的帐户并查看控制台来查看它们 我已经改变了appengine web xml到项目的应用程序 ID 如果我将其更改为 GAE 上第一个创建的

随机推荐

  • power query 如何实现group_concat功能

    power query 分组合并展示 网上答案 https www cnblogs com wsmwsm p 12910590 html 答案 用powerquery 第一步 从表格进入pq界面 第二步 将kid2name列和id列都转换成
  • redis:redis cluster集群实践

    redis集群参数配置 我们后面会部署一个Redis集群作为例子 在那之前 先介绍一下集群在redis conf中的参数 cluster enabled
  • python编译报错 ImportError: cannot import name ‘jaccard_similarity_score‘

    报错如下 问题代码 import numpy as np from sklearn metrics import jaccard similarity score y pred 0 2 1 3 y true 0 1 2 3 print ja
  • 认识传输层(UDP与TCP)

    传输层主要负责数据能够从发送端发送到接收端 要正确传输就要明确发送端和接收端 这时候IP地址和端口号一起就可以确定一端了 那么他们是怎么唯一标识的呢 1 端口号 port 端口号唯一标识一个主机上进行通信的不同应用程序 在TCP IP协议中
  • flutter Stack超出底图范围后,关闭按键点击效果失效

    既然是关闭按键超出了底图范围导致不能点击 那就让关闭按键不超出就可以了 可以给底图再添加一个大点的底图 这两个底图作为一个整体 关闭按键就可以点击了 如图 下面添加了一个黑色的底图用来扩充原来的底图范围 白色关闭按键就可以点击得到了 ret
  • 计算机硬件耗电,电脑中最耗电的三个硬件排行:这答案意料之外的情理之中!...

    众所周知 只要电脑工作的话就肯定会耗电 那么问题就来了 在电脑中哪个硬件的耗电量是最高的呢 耗电的多少又会有哪些标准进行判断呢 就像笔者的文章中反复强调的 我们既要知道结果但同时还需要知道 为什么 笔者今天就继续用打破砂锅问到底的死磕精神来
  • echarts地图map

    在vue中使用echarts绘制图表 npm install echarts save 全局安装echarts 具体代码及注释如下
  • mac上的matlab的设置工具箱cvx

    cvx的下载地址 http cvxr com cvx download 首先怎么做到在终端运行matlab程序呢 打开终端 vi bash profile 进行配置 加入 export PATH PATH Applications MATL
  • ixp协议服务器,ipx协议中的“内部网络号”是什么意思?

    1 IPX的协议构成 IPX协议簇包括如下主要协议 IPX 第三层协议 用来对通过互联网络的数据包进行路由选择和转发 它指定一个无连接的数据报 相当于TCP IP协议簇中的IP协议 SPX 顺序包交换 Sequenced Packet Ex
  • angular编译版本冲突解决办法总结

    刚刚涉足angular 对于node npm typescript等都不太熟悉 网上下载别人源码一编译 报一堆英文错误 死了的心都有了 先来感受一下吧 经过两天的踩坑 东看看 西查查 终于算是解决了目前项目的错误 虽然不知道为什么 但是可以
  • 三元运算符判断字符串是否为空

    有一个变量String userId 判断是否为null 如果为null 就赋值为空串 否则就不变 用if条件写是 if null userId userId 想用三元运算符写 常见错误写法 userId null userId 这样是错误
  • html禁止自动填充input表单的完美解决办法

    提交登陆等表单时 允许记录了密码则会保存起来 且每次都会自动填充入input 我们有时候不需要自动填充 试过网上的各种方法都没能完美解决 最后终于找到解决办法 废话不多说 直接上代码
  • STM8S105K4T6硬件IIC调试小结

    1 IIC初始化 具体时钟设置参考此篇文章 https blog csdn net u014397533 article details 46495905 void I2C Init void I2C CR1 0x00 禁止I2C外设 此句
  • 日本语语料库

    来自 日语语料库建设的现状综述 上海外国语大学 毛文伟 2009年 1 EDR语料库 EDR 该语料库由日本电子化辞书研究所开发 并于1995年推出 素材选自新闻报道和杂志 规模为 20 万句 另有 10 万 句左右的英语语料 在原始语料的
  • 深度学习图像融合 合成 协调笔记

    目录 图像合成最新资料汇总1 图像合成最新资料汇总2 图像渲染 pip install poetry
  • 【Espruino】NO.05 按键是你的仆人

    http blog csdn net qwert1213131 article details 27104341 本文属于个人理解 能力有限 纰漏在所难免 还望指正 小鱼有点电 按键 生活中随处可见 手机 电脑 家用电器 用来执行各种功能
  • Linux中用stat命令查看文件时3个时间点解析

    有些时候 我们需要使用stat命令来查看文件的详细信息 另外联想下 ls l命令显示的是什么时间 touch命令修改文件的时间戳 修改的又是什么时间 在这里我们一起来试验下 首先 我们来看下stat情况 如图所示 会出现3个类型的时间 分别
  • CPU时间与系统时间(CPU time and wall clock time)

    CPU时间是指一段程序在CPU上面运行消耗的时间 也是内核时间 kernel time 在Linux Unix系统里面 C 程序的COU时间可以用一些第三方的库提供的函数测出 但是在Windows系统里面 没有可以直接使用的第三方函数 在这
  • Session和Cookie实现购物车

    来自森大科技官方博客 http www cnsendblog com index php p 342 GPS平台 网站建设 软件开发 系统运维 找森大网络科技 http cnsendnet taobao com 使用Session和Cook
  • 自定义Mybatis框架

    目录 自定义Mybatis分析 轮子缺少的配件 组装轮子 制定骨架 解析配置文件 类关系梳理 创建默认实现类 实现基于注解的查询 目录结构 流程图 通过快速入门示例 Mybatis快速入门 我们发现使用 mybatis 是非常容易的一件事情