手敲MyBatis(十三章)-返回Insert操作自增索引值

1.目的

这一章的目的主要是插入语句以后返回插入记录的id,因为插入语句可分为要返回记录id,不要返回记录id的以及不同数据源类型执行的时机也不同(如:oracle不支持主键,需要先插入序列再增加,Mysql支持主键增加一条记录就会有索引)。

如下图,insert里包含selectKey,由selectKey去执行查询此次新增的id记录,我们看到selectKey标签上的属性有keyProperty、order、resultType。

  • keyProperty这个是把返回索引的索引放入id里
  • order为after则是在insert后执行selectKey
  • resultType则是查询返回的类型,示例里为Long

我们最主要的目的就是解析这样的selectKey,然后存入MappedStatement里到时执行时使用,执行Update时则执行selectKey的方法,最后在activity实体id赋值执行完的记录id。

 2.设计说明

标黄色的是修改的,其余都是新增的方法。

 1.构建时(builder)

在构建(builder)时,我们需要解析下selectKey的标签,而selectKey是在语句里,所以需要更改XMLStatementBuilder类添加解析selectKey的标签。最后把解析好的标签放入到addMappedStatement(),这里的动作是两次addMappedStatement(),为什么是两次呢?ps:可以看下面两个图。

  • 第一次,SqlCommandType是“SELECT”,代表selectKey本身的语句,需要存储一次MappedStatement
  • 第二次,SqlCommandType是“INSERT”,代表是selectKey父级的INSERT标签,它下面要包含这个selectKey的MappedStatement信息,然后如果有selectKey则创建SelectKeyGenerator对象,需要再一次存储MappedStatement。

ps此处看不懂可以调试全局看下,多看几遍就懂了为什么这样设计了。

2.selectKey执行时(executor keygen)

2.1 定义接口:

针对上述情况,需要定义接口KeyGenerator,定义方法为processBefore()(针对Oracle不支持主键的)和processAfter()(支持主键的,如MySql)

2.2 实现接口:

NoKenGenerator和SelectKeyGenerator,不要求返回记录的就执行NoKenGenerator(实现方法什么都不操作)。SelectKeyGenerator的实现则是执行查询索引操作,并将结果添加到insert的操作的实体里,这样就得到了索引。

3.执行时(executor)

我们存储到MappedStatement以后就要流转到执行时,因为SelectKey是查询,所以在Executor时

添加了重载的query()方法。

BaseExecutor实现新的query()方法,拿到了sql语句后,调用原有的doQuery,由SimpleExecutor类实现,此时就会调用BaseStatementHandler构造方法,这里新添加了generateKeys()方法,主要是检查下是否需要在insert前执行selectKey(如:Mysql是insert后,Oracle是insert前)。

执行了新增方法时,则需要添加调用KeyGenerator的processAfter()方法,这个方法依然要检查是否在insert后执行selectKey。

4.结果集的更改(resultset)

由于之前的结果集是只处理bean对象, 一般返回selectKey需要的基本类型,如ID是Long,所以这里需要添加createPrimitiveResultObject()方法获取类型处理器。

在createResultObject()此方法里则需要判断是否是基本类型,如果基本类型则调用createPrimitiveResultObject()。其余类型则还按之前代码走。

5.connection,连接的更改

由于要两个语句用一个连接如果两个连接则无法返回id索引,所以需要在获取连接处判断下是否有连接,如果有就不创建,没有就创建。

3.代码

3.1 selectKey执行操作

包:package cn.bugstack.mybatis.executor.keygen;

添加接口KeyGenerator,主要是用来执行SelectKey查询操作的,此接口定义了两个方法,

processBefore:是在不支持主键需要获取序列时调用,执行insert语句前调用

processAfter:是在支持主键在执行insert语句后嗲用

public interface KeyGenerator {/*** 针对Sequence主键而言,在执行insert sql前必须指定一个主键值给要插入的记录,* 如Oracle、DB2,KeyGenerator提供了processBefore()方法。*/void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);/*** 针对自增主键的表,在插入时不需要主键,而是在插入过程自动获取一个自增的主键,* 比如MySQL、PostgreSQL,KeyGenerator提供了processAfter()方法*/void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}

实现类SelectKeyGenera,有selectKey时实现执行selectKey的逻辑。

// step13新增
public class SelectKeyGenerator implements KeyGenerator {public static final String SELECT_KEY_SUFFIX = "!selectKey";private boolean executeBefore;private MappedStatement keyStatement;public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {this.executeBefore = executeBefore;this.keyStatement = keyStatement;}@Overridepublic void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {if (executeBefore) {processGeneratedKeys(executor, ms, parameter);}}@Overridepublic void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {if (!executeBefore) {processGeneratedKeys(executor, ms, parameter);}}/*** 执行selectKey的SQL语句,执行完毕后把返回的id结果放入对应实体中。*/private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {try {if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {String[] keyProperties = keyStatement.getKeyProperties();final Configuration configuration = ms.getConfiguration();final MetaObject metaParam = configuration.newMetaObject(parameter);if (keyProperties != null) {Executor keyExecutor = configuration.newExecutor(executor.getTransaction());List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);if (values.size() == 0) {throw new RuntimeException("SelectKey returned no data.");} else if (values.size() > 1) {throw new RuntimeException("SelectKey returned more than one value.");} else {MetaObject metaResult = configuration.newMetaObject(values.get(0));if (keyProperties.length == 1) {if (metaResult.hasGetter(keyProperties[0])) {setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));} else {setValue(metaParam, keyProperties[0], values.get(0));}} else {handleMultipleProperties(keyProperties, metaParam, metaResult);}}}}} catch (Exception e) {throw new RuntimeException("Error selecting key or setting result to parameter object. Cause: " + e);}}private void handleMultipleProperties(String[] keyProperties,MetaObject metaParam, MetaObject metaResult) {String[] keyColumns = keyStatement.getKeyColumns();if (keyColumns == null || keyColumns.length == 0) {for (String keyProperty : keyProperties) {setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));}} else {if (keyColumns.length != keyProperties.length) {throw new RuntimeException("If SelectKey has key columns, the number must match the number of key properties.");}for (int i = 0; i < keyProperties.length; i++) {setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));}}}private void setValue(MetaObject metaParam, String property, Object value) {if (metaParam.hasSetter(property)) {metaParam.setValue(property, value);} else {throw new RuntimeException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");}}
}

实现类NoKeyGenerator,在没有selectKey时的逻辑也就是不写具体的逻辑。

public class NoKeyGenerator implements KeyGenerator {@Overridepublic void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {// Do Nothing}@Overridepublic void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {// Do Nothing}
}

3.2 builder操作部分的修改

XMLStatementBuilder修改parseStatementNode(),添加了解析selectKey的节点方法processSelectKeyNodes();

如果有多个就遍历每个节点信息存储到MappedStatement下

public class XMLStatementBuilder extends BaseBuilder {// 省略其他public void parseStatementNode() {// 省略其他//  解析<selectKey> step-13 新增processSelectKeyNodes(id, parameterTypeClass, langDriver);}/*** 解析selectKey标签*/private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {// 得到selectKey标签List<Element> selectKeyNodes = element.elements("selectKey");parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver);}/*** for循环遍历selectKey标签* 取出selectKey的父id拼接select的Key标识*/private void parseSelectKeyNodes(String parentId, List<Element> list, Class<?> parameterTypeClass, LanguageDriver langDriver) {for (Element nodeToHandle : list) {String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver);}}/*** 开始解析每一个selectKey标签的内容,并存储MappedStatement里* <selectKey keyProperty="id" order="AFTER" resultType="long">* SELECT LAST_INSERT_ID()* </selectKey>*/private void parseSelectKeyNode(String id, Element nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver) {String resultType = nodeToHandle.attributeValue("resultType");// 得到resultType类型Class<?> resultTypeClass = resolveClass(resultType);// 执行前还是执行后boolean executeBefore = "BEFORE".equals(nodeToHandle.attributeValue("order", "AFTER"));String keyProperty = nodeToHandle.attributeValue("keyProperty");// defaultString resultMap = null;KeyGenerator keyGenerator = new NoKeyGenerator();// 解析成SqlSource,DynamicSqlSource/RawSqlSourceSqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);// SELECT标签SqlCommandType sqlCommandType = SqlCommandType.SELECT;// 调用助手类builderAssistant.addMappedStatement(id,sqlSource,sqlCommandType,parameterTypeClass,resultMap,resultTypeClass,keyGenerator,keyProperty,langDriver);// 给id加上namespace前缀id = builderAssistant.applyCurrentNamespace(id, false);// 存放键值生成器配置MappedStatement keyStatement = configuration.getMappedStatement(id);// 将KeyGenerator放入map中configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));}
}

MapperAnnotationBuilder类,注解版的构建也是一样的,需要修改解析的语句,方法为parseStatement()

public class MapperAnnotationBuilder {private void parseStatement(Method method) {// 省略其他// step-13 新增-----------------------------------------------------------KeyGenerator keyGenerator;String keyProperty = "id";if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();} else {keyGenerator = new NoKeyGenerator();}// step-14 新增-----------------------------------------------------------boolean isSelect = sqlCommandType == SqlCommandType.SELECT;String resultMapId = null;if (isSelect) {resultMapId = parseResultMap(method);}// step-13 部分参数新增( keyGenerator,keyProperty,)-------------------------// 调用助手类assistant.addMappedStatement(mappedStatementId,sqlSource,sqlCommandType,parameterTypeClass,resultMapId,getReturnType(method),keyGenerator,keyProperty,languageDriver);}
}

因为MappedStatement要存储数据,所以MappedStatement要加相应的字段,keyGenerator以及keyProperties和keyColumns

public class MappedStatement {// 其余省略// step-13 新增private String resource;private KeyGenerator keyGenerator;private String[] keyProperties;private String[] keyColumns;public static class Builder {public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, SqlSource sqlSource, Class<?> resultType) {mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();}public Builder resource(String resource) {mappedStatement.resource = resource;return this;}public Builder keyGenerator(KeyGenerator keyGenerator) {mappedStatement.keyGenerator = keyGenerator;return this;}public Builder keyProperty(String keyProperty) {mappedStatement.keyProperties = delimitedStringToArray(keyProperty);return this;}}public String[] getKeyColumns() {return keyColumns;}public String[] getKeyProperties() {return keyProperties;}public KeyGenerator getKeyGenerator() {return keyGenerator;}
}

MapperBuilderAssistant类:此类修改了addMappedStatement()方法需要增加几个参数,并把参数放入MappedStatement中。

  public MappedStatement addMappedStatement(String id,SqlSource sqlSource,SqlCommandType sqlCommandType,Class<?> parameterType,String resultMap,Class<?> resultType,KeyGenerator keyGenerator,String keyProperty,LanguageDriver lang) {// 给id加上namespace前缀:cn.bugstack.mybatis.test.dao.IUserDao.queryUserInfoByIdid = applyCurrentNamespace(id, false);MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);// step-13新增/添加三个属性statementBuilder.resource(resource);statementBuilder.keyGenerator(keyGenerator);statementBuilder.keyProperty(keyProperty);// 结果映射,给 MappedStatement#resultMapssetStatementResultMap(resultMap, resultType, statementBuilder);MappedStatement statement = statementBuilder.build();// 映射语句信息,建造完存放到配置项中configuration.addMappedStatement(statement);return statement;}

3,3 insert执行SQL部分修改

包:package cn.bugstack.mybatis.executor

Executor接口需要新增个query方法,SelectKeyGenerator执行查询时使用。

  // step-13新增-----------------------------------<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

BaseExecutor:基础实现类去实现这个query方法,然后调用原有的其他的query方法,然后顺着会调用SimpleExecutor的doQuery()这里的处理没有改变,所以就不提供代码拉。这块就和查询逻辑一样。

    // step-14新增-----------------------------------------@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);return query(ms, parameter, rowBounds, resultHandler, boundSql);}

在上面SimpleExecutor执行doQuery()时,需要调用到BaseStatementHandler的构造方法,此时就需要检查一下是否需要在insert前要执行,如果需要就执行SelectKey,selectKey又会调用上边的query进行查询操作,如果否则不会执行任何操作,判断则在keyGenerator.processBefore()处理。

public abstract class BaseStatementHandler implements StatementHandler {public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {if (boundSql == null) {// 针对Sequence主键而言,在执行insert sql前必须指定一个主键值给要插入的记录。generateKeys(parameterObject);}}// 针对Sequence主键而言,在执行insert sql前必须指定一个主键值给要插入的记录。KeyGenerator#processBeforeprotected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processBefore(executor, mappedStatement, null, parameter);}
}

然后就要修改下执行insert语句了,在Mybatis中所有的insert、del、update,都统一为update方法,所以我们需要修改下update(),修改为执行完insert操作后调用keyGenerator.processAfter(),后置处理(有主键情况下使用),修改如下:

PreparedStatementHandler类

包:package cn.bugstack.mybatis.executor.statement;

    @Overridepublic int update(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();// step-13新增----------------------------------------// 执行 selectKey 语句int rows = ps.getUpdateCount();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);// step-13新增----------------------------------------return rows;}

最后最重要的是,执行insert再执行查询当前插入记录的主键,必须是一个连接,如果是两个连接结果就是空,所以需要更改获取连接的操作,这里很简单,判断下就行了。

修改的包:cn.bugstack.mybatis.transaction.jdbc

修改的类:JdbcTransaction

    @Overridepublic Connection getConnection() throws SQLException {// step-13新增------------------------------------// 本章节新增;多个SQL在同一个JDBC连接下,才能完成事务特性if (connection != null) {return connection;}// step-13新增------------------------------------connection = dataSource.getConnection();connection.setTransactionIsolation(level.getLevel());connection.setAutoCommit(autoCommit);return connection;}

4.准备测试

xml如下:

     <insert id="insert" parameterType="cn.bugstack.mybatis.test.po.Activity">INSERT INTO activity(activity_id, activity_name, activity_desc, create_time, update_time)VALUES (#{activityId}, #{activityName}, #{activityDesc}, now(), now())<selectKey keyProperty="id" order="AFTER" resultType="long">SELECT LAST_INSERT_ID()</selectKey></insert>

单元测试如下:

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);private SqlSession sqlSession;@Beforepublic void init() throws IOException {// 1. 从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));sqlSession = sqlSessionFactory.openSession();}@Testpublic void test_insert() {// 1. 获取映射器对象IActivityDao dao = sqlSession.getMapper(IActivityDao.class);Activity activity = new Activity();activity.setActivityId(10004L);activity.setActivityName("测试活动");activity.setActivityDesc("测试数据插入");activity.setCreator("xdf");// 2. 测试验证Integer res = dao.insert(activity);sqlSession.commit();logger.info("测试结果:count:{} idx:{}", res, JSON.toJSONString(activity.getId()));}

执行结果: 

执行的两条都展示了正确的索引。

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

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

相关文章

SQL指南:掌握日期函数来查询和管理数据

文章目录 1. 引言2. 建立数据库表2.1 建表语句2.2 数据插入 查询案例3.1 查询当前日期的订单3.2 查询过去一周内的订单3.3 查询明天的日期3.4 查询今年的订单3.5 查询特定月份的订单 总结 1. 引言 在数据库管理中&#xff0c;处理日期和时间是一项基本但重要的任务。本指南将通…

数智金融技术峰会|数新网络受邀分享《金融信创湖仓一体数据平台架构实践》,敬请期待

12月23日&#xff0c;数新网络参加DataFunSummit 2023&#xff1a;数智金融技术峰会。会上&#xff0c;数新CTO原攀峰将为大家带来《金融信创湖仓一体数据平台架构实践》 主题分享。 本次峰会由DataFun联合火山引擎、蓝驰等知名企业举办&#xff0c;将共同为大家带来一场数智金…

玩转Instagram Shop只需要学会这些功能

Instagram Shop作为Instagram下属的电商购物平台。用户可以通过浏览Instagram上的推荐产品和品牌&#xff0c;在无需离开应用的情况下了解并购买新的商品。对于经常使用Instagram的用户来说是个很便捷的购物渠道。面对这个新渠道&#xff0c;我们又该如何玩转它呢。这篇文章就会…

【沐风老师】3dMax篮球建模方法详解

3dMax足球、排球和篮球建模系列之&#xff1a;篮球建模。对于足球和排球建模&#xff0c;思路是从一个基础模型开始&#xff0c;利用这个基础模型与最终的足球&#xff08;或排球&#xff09;模型的某些相似之处&#xff0c;经过修改编辑&#xff0c;最终完成目标模型的建模。但…

ansible的playbook

1、playbook的组成部分 &#xff08;1&#xff09;task任务&#xff1a;在目标主机上执行的操作&#xff0c;使用模块定义这些操作&#xff0c;每个任务都是一个模块的调用 &#xff08;2&#xff09;variables变量&#xff1a;存储和传递数据&#xff08;变量可以自定义&…

Java可变参数(学习推荐版,通俗易懂)

定义 可变参数本质还是一个数组 示例代码 注意事项 1.形参列表中&#xff0c;可变参数只能有一个 2.可变参数必须放在形参列表的最后面 注意是最后面。 name也可以为int类型

【C#】TimeSpan

文章目录 概述属性时间计算拓展来源 概述 TimeSpan结构&#xff1a;表示一个时间间隔。 它含有以下四个构造函数&#xff1a; TimeSpan(Int64)将 TimeSpan结构的新实例初始化为指定的刻度数。&#xff08;DateTime.Tick:是计算机的一个计时周期&#xff0c;单位是一百纳秒&…

3. 行为模式 - 迭代器模式

亦称&#xff1a; Iterator 意图 迭代器模式是一种行为设计模式&#xff0c; 让你能在不暴露集合底层表现形式 &#xff08;列表、 栈和树等&#xff09; 的情况下遍历集合中所有的元素。 问题 集合是编程中最常使用的数据类型之一。 尽管如此&#xff0c; 集合只是一组对象的…

数据结构(八):图介绍及面试常考算法

一、图介绍 1、定义 图由结点的有穷集合V和边的集合E组成。其中&#xff0c;结点也称为顶点。一对结点&#xff08;x&#xff0c; y&#xff09;称为边&#xff08;edge&#xff09;&#xff0c;表示顶点x连接到顶点y。边可以包含权重/成本&#xff0c;显示从顶点x到y所需的成…

深入解析Python装饰器及*args, **kwargs的妙用

深入解析Python装饰器及*args, **kwargs的妙用 简介&#xff1a; ​ 装饰器&#xff08;Decorator&#xff09;是 Python 中一种强大的语法特性&#xff0c;它允许在不修改原始函数代码的情况下&#xff0c;动态地扩展函数的功能。装饰器是函数或类&#xff0c;用于包装其他函…

云上荆楚丨云轴科技ZStack成功实践精选(湖北)

湖北自古以来有九省通衢的美称&#xff0c;地处长江中游&#xff0c;富有荆楚之美誉&#xff0c;灵秀之蕴意。2022年湖北数字经济强省三年行动计划正式印发&#xff0c;计划到“十四五”末&#xff0c;数字经济核心产业增加值力争达到7000亿元&#xff0c;占GDP的比重超过12%。…

《每天一分钟学习C语言·七》指针、字节对齐等

1、 对于二维数组如a[3][4]可以当做有三个元素的一维数组&#xff0c;每个元素包含四个小元素。 2、 printf(“%-5d”, i); //负号表示左对齐&#xff0c;5d表示空五个光标的位置 3、 栈&#xff1a;先进后出&#xff0c;堆&#xff1a;先进先出 4、 &#xff08;1&#xff…

位运算:Leetcode137.只出现一次的数字(2)

题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [2,2,3,2] 输出&#xff1a;3示例 2&#xff1a; 输入&…

《工具箱-SVN》SVN安装、备份、迁移教程

文章目录 一、服务器搭建SVN1.检查SVN是否存在2.安装SVN3.创建版本库4.创建版本库存放文件地址5.修改配置文件5.1 vim authz5.2 vim passwd5.3 vim svnserve.conf 6.启动并查看SVN7.SVN Checkout8.SVN Update9.SVN Commit 二、SVN-无法连接主机&#xff0c;目标计算机积极拒绝&…

案例 | 电源自动检测测试系统为某电子科技公司定制电源测试解决方案

一、测试背景 陕西某电子科技公司是一家专业生产设计军品电源、集成电路以及电子元器件的高新技术企业&#xff0c;公司虽有一套半自动ATE测试系统&#xff0c;但使用过程繁琐复杂且无法满足日益增长的测试需求&#xff0c;因此公司现需要一套更加优秀的全自动电源测试系统来应…

工具系列:PyCaret介绍_多分类代码示例

&#x1f44b; 工具系列&#xff1a;PyCaret介绍_多分类代码示例 PyCaret 介绍 PyCaret是一个开源的、低代码的Python机器学习库&#xff0c;可以自动化机器学习工作流程。它是一个端到端的机器学习和模型管理工具&#xff0c;可以大大加快实验周期并提高生产效率。 与其他开…

HDFS NFS Gateway(环境配置,超级详细!!)

&#x1f42e;博主syst1m 带你 acquire knowledge&#xff01; ✨博客首页——syst1m的博客&#x1f498; &#x1f618;《CTF专栏》超级详细的解析&#xff0c;宝宝级教学让你从蹒跚学步到健步如飞&#x1f648; &#x1f60e;《大数据专栏》大数据从0到秃头&#x1f47d;&…

【论文阅读】FreeU: Free Lunch in Diffusion U-Net

FreeU: 无需训练直接提升扩散模型生成效果。 paper&#xff1a;https://arxiv.org/abs/2309.11497 code&#xff1a;GitHub - ChenyangSi/FreeU: FreeU: Free Lunch in Diffusion U-Net 1. 介绍 贡献&#xff1a; •研究并揭示了U-Net架构在扩散模型中去噪的潜力&#xff0…

Redis单机、主从、哨兵、集群配置

单机配置启动 Redis安装 下载地址&#xff1a;Download | Redis 安装步骤&#xff1a; 1: 安装gcc编译器&#xff1a;yum install gcc 2: 将下载好的redis‐5.0.3.tar.gz文件放置在/usr/local文件夹下&#xff0c;并解压redis‐5.0.3.tar.gz文件 wget http://download.re…

react生命周期详解,代码示例(新生命周期,与旧生命周期对比)

旧生命周期&#xff1a;https://blog.csdn.net/kkkys_kkk/article/details/135130549?spm1001.2014.3001.5501 目录 React 生命周期中常见的坑 为什么要移除 “will” 相关生命周期方法呢&#xff1f; Fiber是什么 新生命周期图示 新增生命周期与功能变化 完整生命周期…