• 概述

    • 事务管理关键抽象
  • Spring事务管理的实现类
    • Spring JDBC 和MybBatis的事务管理器的配置
    • JPA的事务管理器的配置
    • Hibernate的事务管理器的配置
    • JTA 的事务管理器的配置
  • 事务同步管理器
  • 事务的传播行为
    • 示例
  • 编程式的事务管理
    • 示例

概述

Spring为事务管理提供了一致的编程模板,在高层次建立了统一的事务抽象。也就是说,不管选择Spring JDBC、Hibernate 、JPA 还是iBatis,Spring都让我们可以用统一的编程模型进行事务管理。

类似Spring DAO 为不同的持久化技术实现提供了模板类一样,Spring事务管理也提供了事务模板类TransactionTemplate。 通过TransactionTemplate并配合使用事务回调TransactionCallback指定具体的持久化操作,就可以 通过编程的方式实现事务管理,而无须关注资源获取、复用、释放、事务同步和异步处理等操作。

Spring事务管理的亮点在于声明式事务管理,Spring允许通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。


事务管理关键抽象

在Spring事务管理SPI(Service Provider Interface)的抽象层主要包括3个接口,分别是PlatformTransactionManager、TransactionDefinition和TransactionStatus。 都在org.springframework.transaction包中。

  • TransactionDefinition用于描述事务的隔离级别、超时时间、是否为只读事务和事务传播规则等控制事务具体行为的事务属性,这些事务属性可以通过XML配置或注解描述提供,也可以通过手工编程的方式设置。

  • PlatformTransactionManager根据TransactionDefinition提供的事务属性配置信息,创建事务,并用TransactionStatus描述这个激活事务的状态。


Spring事务管理的实现类

spring将事务管理委托底层具体的持久化实现框架去完成,因此针对不同的框架spring有的不同的接口实现类.

事务 说明
org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时,使用该事务管理器
org.springframework.orm.hibernateX.HibernateTransactionManager 使用HibernateX版本时使用该事务管理器
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用SpringJDBC或MyBatis等基于DataSource数据源的持久化技术时,使用该事务管理器
org.springframework.orm.jdo.JdoTransactionManager 使用JDO进行持久化时,使用该事务管理器
org.springframework.transaction.jta.JtaTransactionManager 具有多个数据源的全局事务使用该事务管理器(不管采用何种持久化技术)

要实现事务管理,首先要在Spring中配置好相应的事务管理器,为事务管理器指定数据资源及一些其他事务管理控制属性。

下面介绍一下几个常见的事务管理器的配置

Spring JDBC 和MybBatis的事务管理器的配置

Spring JDBC 和MybBatis都是基于数据源的Connection访问数据库,所有都可以使用DataSourceTransactionManager, 配置如下

<!--引用外部的Properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/><!--配置一个数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}"p:username="${jdbc.username}"p:password="${jdbc.password}"/><!--基于数据源的事务管理器,通过属性引用数据源-->
<bean id="transactionManager"           class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"/>

JPA的事务管理器的配置

要配置一个JPA事务管理器,必须现提供一个DataSource,然后配置一个EntityManagerFactory,最后才配置JpaTransationManager.

.......<!--通过dataSource-ref指定一个数据源-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"p:dataSource-ref="dataSource"/>......
</bean><!--指定实体管理器--><bean id="transactionManger" class="org.springframework.orm.jpa.JpaTransactionManager"p:entityManagerFacotry-ref="entityManagerFactory"/>

Hibernate的事务管理器的配置

Spring4.0已经取消了对Hibernate3.6之前的版本支持,并全面支持Hibernate5.0. 因此,只为Hibernate3.6+提供事务管理器。

以Hibernate4.0为例

....
<!--通过dataSource-ref引用数据源 和 Hibernate配置文件 及其他属性-->
<bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"p:dataSource-ref="dataSource"p:mappingResources="classpath:Artisan.hbm.xml"><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.generate_statistics">true</prop></props></property>
</bean><bean id="transactionManager"           class="org.springframework.orm.hibernate4.HibernateTransactionManager"p:sessionFactory-ref="sessionFactory"/>

JTA 的事务管理器的配置

如果希望在JavaEE容器中使用JTA,则将通过JNDI和Spring的JtaTransactionManager获取一个容器的DataSource。

<!--通过jee命名空间获取Java EE应用服务器容器中的数据源-->
<jee:jndi-lookup id="accountDs" jndi-name="java:comp/env/jdbc/account"/>
<jee:jndi-lookup id="orderDs" jndi-name="java:comp/env/jdbc/account"/><!--指定JTA事务管理器。-->
<bean id="transactionManager"class="org.springframework.transaction.jta.JtaTransactionManager"/>

事务同步管理器

Spring将JDBC的Connection、Hibernate的Session等访问数据库的连接或者会话对象统称为资源,这些资源在同一时刻是不能多线程共享的。

为了让DAO、Service类可能做到singleton, Spring的事务同步管理类org.springframework.transaction.support.TransactionSynchronizationManager使用ThreadLocal为不同事务线程提供了独立的资源副本,同时维护事务配置的属性和运行状态信息

事务同步管理器是Spring事务管理的基石,不管用户使用的是编程式事务管理,还是声明式事务管理,都离不开事务同步管理器。

Spring框架为不同的持久化技术提供了一套从TransactionSynchronizationManager中获取对应线程绑定资源的工具类

持久化技术 线程绑定资源获取工具
Spring JDBC或者MyBatis org.springframework.jdbc.datasource.DataSourceUtils
HibernateX.0 org.springframework.orm.hibernateC.SessionFactoryUtils
JPA org.springframework.orm.jpa.EntityManagerFactoryUtils
JDO org.springframework.orm.jdo.PersistenceManagerFactoryUtils

这些工具类都提供了静态的方法,通过这些方法可以获取和当前线程绑定的资源,如

  • DataSourceUtils.getConnection (DataSource
    dataSource)可以从指定的数据源中获取和当前线程绑定的Connection

  • Hibernate的SessionFactoryUtils.getSession (SessionFactory
    sessionFactory, boolean allowCreate)则从指定的SessionFactory中获取和当前线程绑定的Session。

    当需要脱离模板类,手工操作底层持久技术的原生API时,就需要通过这些工具类获取线程绑定的资源,而不应该直接从DataSource或SessionFactory中获取。因为后者不能获得和本线程相关的资源,因此无法让数据操作参与到本线程相关的事务环境中。

这些工具类还有另外一个重要的用途:将特定异常转换为Spring的DAO异常。

Spring为不同的持久化技术提供了模板类,模板类在内部通过资源获取工具类间接访问TransactionSynchronizationManager中的线程绑定资源。所以,如果Dao使用模板类进行持久化操作,这些Dao就可以配置成singleton。如果不使用模板类,也可直接通过资源获取工具类访问线程相关的资源。

我们来开下TransactionSynchronizationManager的面纱:

TransactionSynchronizationManager将Dao、Service类中影响线程安全的所有“状态”统一抽取到该类中,并用ThreadLocal进行替换,从此Dao(必须基于模板类或资源获取工具类创建的Dao)和Service(必须采用Spring事务管理机制)摘掉了非线程安全的帽子,完成了脱胎换骨式的身份转变。


事务的传播行为

当我们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务 环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个完整的业务操作,因此就会产生服务接口方法嵌套调用的情况, Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。

事务传播是Spring进行事务管理的重要概念,其重要性怎么强调都不为过。但是事务传播行为也是被误解最多的地方,在本文里,我们将详细分析不同事务传播行为的表现形式,掌握它们之间的区别。

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC 3.0,并且实现者需要支持保存点事务机制。

示例

当服务接口方法分别使用表1中不同的事务传播行为,且这些接口方法又发生相互调用的情况下,大部分组合都是一目了然,容易理解的。但是,也存在一些容易引起误解的组合事务传播方式。

下面,我们通过两个具体的服务接口的组合调用行为来破解这一难点。这两个服务接口分别是UserService和ForumService, UserSerice有一个addCredits()方法,ForumSerivce#addTopic()方法调用了 UserSerice#addCredits()方法,发生关联性服务方法的调用:

@Service
public class ForumService {private UserService userService;// ①调用其它服务接口的方法public void addTopic() {// ②被关联调用的业务方法userService.addCredits();}public void setUserService(UserService userService) {this.userService = userService;}}

嵌套调用的事务方法 : 对Spring事务传播行为最常见的一个误解是:当服务接口方法发生嵌套调用时,被调用的服务方法只能声明为 PROPAGATION_NESTED。这种观点犯了望文生义的错误,误认为PROPAGATION_NESTED是专为方法嵌套准备的。这种误解遗害不 浅,执有这种误解的开发者错误地认为:应尽量不让Service类的业务方法发生相互的调用,Service类只能调用DAO层的DAO类,以避免产生嵌 套事务。

其实,这种顾虑是完全没有必要的,PROPAGATION_REQUIRED已经清楚地告诉我们:事务的方法会足够“聪明”地判断上下文是否已经存在一个事务中,如果已经存在,就加入到这个事务中,否则创建一个新的事务。

依照上面的例子,假设我们将ForumService#addTopic()和UserSerice#addCredits()方法的事务传播行为都设置为PROPAGATION_REQUIRED,这两个方法将运行于同一个事务中。

将ForumService#addTopic()设置为PROPAGATION_REQUIRED时, UserSerice#addCredits()设置为PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、 PROPAGATION_MANDATORY时,运行的效果都是一致的(当然,如果单独调用addCredits()就另当别论了)。

当addTopic()运行在一个事务下(如设置为PROPAGATION_REQUIRED),而addCredits()设置为 PROPAGATION_NESTED时,如果底层数据源支持保存点,Spring将为内部的addCredits()方法产生的一个内嵌的事务。如果 addCredits()对应的内嵌事务执行失败,事务将回滚到addCredits()方法执行前的点,并不会将整个事务回滚。内嵌事务是内层事务的一 部分,所以只有外层事务提交时,嵌套事务才能一并提交。

嵌套事务不能够提交,它必须通过外层事务来完成提交的动作,外层事务的回滚也会造成内部事务的回滚。

嵌套事务和新事务

PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED也是容易混淆的两个传播行为。PROPAGATION_REQUIRES_NEW 启动一个新的、和外层事务无关的“内部”事务。该事务拥有自己的独立隔离级别和锁,不依赖于外部事务,独立地提交和回滚。当内部事务开始执行时,外部事务 将被挂起,内务事务结束时,外部事务才继续执行。

由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于:

  • PROPAGATION_REQUIRES_NEW 将创建一个全新的事务,它和外层事务没有任何关系,
  • 而 PROPAGATION_NESTED 将创建一个依赖于外层事务的子事务,当外层事务提交或回滚时,子事务也会连带提交和回滚。

以下几个问题值得注意:

  • 1.当业务方法被设置为PROPAGATION_MANDATORY时,它就不能被非事务的业务方法调用。

    如将ForumService#addTopic ()设置为PROPAGATION_MANDATORY,如果展现层的Action直接调用addTopic()方法,将引发一个异常。正确的情况是: addTopic()方法必须被另一个带事务的业务方法调用(如ForumService#otherMethod())。所以 PROPAGATION_MANDATORY的方法一般都是被其它业务方法间接调用的。
    
  • 2 当业务方法被设置为PROPAGATION_NEVER时,它将不能被拥有事务的其它业务方法调用。

    假设UserService#addCredits  ()设置为PROPAGATION_NEVER,当ForumService# addTopic()拥有一个事务时,addCredits()方法将抛出异常。所以PROPAGATION_NEVER方法一般是被直接调用的。
    
  • 3 当方法被设置为PROPAGATION_NOT_SUPPORTED时,外层业务方法的事务会被挂起,当内部方法运行完成后,外层方法的事务重新运行。如果外层方法没有事务,直接运行,不需要做任何其它的事。

在Spring声明式事务管理的配置中,事务传播行为是最容易被误解的配置项,原因在于事务传播行为名称(如 PROPAGATION_NESTED:嵌套式事务)和代码结构的类似性上(业务类方法嵌套调用另一个业务类方法).


编程式的事务管理

在实际的应用中很少通过编程来进行事务管理,但是Spring还是为编程式事务管理提供了模板类 TransactionTemplate,以满足一些特殊场合的要求。

TransactionTemplate是线程安全的,因此可以在多个类中共享TransactionTemplate实例进行事务管理。

TransactionTemplate主要有两个方法:

  • public void setTransactionManager(PlatformTransactionManager transactionManager) 设置事务管理器

  • public <T> T execute(TransactionCallback<T> action) throws TransactionException 在TransactionCallback回调接口中定义需要以事务方式组织的数据访问逻辑

TransactionCallback接口中仅有一个方法

protected void doInTransaction(TransactionStatus status)

如果操作不需要返回结果,可以使用TransactionCallback的子接口 TransactionCallbackWithoutResult。

示例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

POJO

package com.xgj.dao.transaction.programTrans;import org.springframework.stereotype.Component;/*** * * @ClassName: Artisan* * @Description: @Component标注的Bean* * @author: Mr.Yang* * @date: 2017年9月18日 下午5:03:47*/@Component
public class Artisan {private String userName;private String password;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;}}
package com.xgj.dao.transaction.programTrans;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;/*** * * @ClassName: ProgramTransService* * @Description: 在实际应用中,很少通过编程的方式来进行事务管理。* * @author: Mr.Yang* * @date: 2017年9月21日 下午3:48:10*/@Service
public class ProgramTransService {private JdbcTemplate jdbcTemplate;private TransactionTemplate transactionTemplate;// 下面两条SQL在一个事务中,第二条故意写错了表名,会执行失败,第一条已经成功的SQL也会回滚private static final String addArtisanSQL = "insert into artisan_user(user_name,password) values(?,?)";private static final String deleteOneArtisanSQL = "delete from artisan_user1 where user_name = 'ArtisanBatch0' ";@Autowiredpublic void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}/*** * * @Title: setTransactionTemplate* * @Description: 通过AOP主动注入transactionTemplate* * @param transactionTemplate* * @return: void*/@Autowiredpublic void setTransactionTemplate(TransactionTemplate transactionTemplate) {this.transactionTemplate = transactionTemplate;}public void operArtisanInTrans(final Artisan artisan) {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {// 需要在事务中执行的逻辑jdbcTemplate.update(addArtisanSQL, artisan.getUserName(),artisan.getPassword());System.out.println("addArtisanSQL  OK ");jdbcTemplate.update(deleteOneArtisanSQL);System.out.println("deleteOneArtisanSQL  OK ");}});}
}

配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.transaction.programTrans" /><!-- 不使用context命名空间,则需要定义Bean <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:spring/jdbc.properties" /> </bean> --><!-- 使用context命名空间,同上面的Bean等效.在xml文件中配置数据库的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /><!-- 配置Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" /><!--基于数据源的事务管理器,通过属性引用数据源--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"/><!-- 配置transactionTemplate模板 -->    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"p:transactionManager-ref="transactionManager"/></beans>

单元测试

package com.xgj.dao.transaction.programTrans;import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class ProgramTransServiceTest {ClassPathXmlApplicationContext ctx = null;@Beforepublic void initContext() {// 启动Spring 容器ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/transaction/programTrans/conf_program_transaction.xml");System.out.println("initContext successfully");}@Testpublic void testProgramTransaction() {Artisan artisan = ctx.getBean("artisan", Artisan.class);artisan.setUserName("trans");artisan.setPassword("123");ProgramTransService programTransService = ctx.getBean("programTransService", ProgramTransService.class);programTransService.operArtisanInTrans(artisan);System.out.println("testProgramTransaction successsfully");}@Afterpublic void closeContext() {if (ctx != null) {ctx.close();}System.out.println("close context successfully");}}

运行结果

第二条因为执行失败,第一条也回滚了,未插入数据, OK。


Spring JDBC-Spring对事务管理的支持相关推荐

  1. Spring JDBC声明式事务管理

    Java事务的类型有三种: (1)JDBC事务:可以将多个 SQL 语句结合到一个事务中.JDBC 事务的一个缺点是事务的范围局限于一个数据库连接.一个 JDBC 事务不能跨越多个数据库. (2)JT ...

  2. 全面分析 Spring 的编程式事务管理及声明式事务管理(转)

    摘要 Spring 的事务管理是 Spring 框架中一个比较重要的知识点,该知识点本身并不复杂,只是由于其比较灵活,导致初学者很难把握.本教程从基础知识开始,详细分析了 Spring 事务管理的使用 ...

  3. 全面分析 Spring 的编程式事务管理及声明式事务管理--转

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  4. java元婴期(21)----java进阶(spring(5)---事务管理AOP事务管理(全自动)spring整合Junit)

    事务管理 事务:一组业务操作ABCD,要么全部成功,要么全部不成功. 特性:ACID 原子性:整体 一致性:完成 隔离性:并发 持久性:结果 隔离问题: 脏读:一个事务读到另一个事务没有提交的数据 不 ...

  5. 在Spring中使用JTA事务管理

    在Spring中使用JTA事务管理 Spring 通过AOP技术可以让我们在脱离EJB的情况下享受声明式事务的丰盛大餐,脱离Java EE应用服务器使用声明式事务的道路已经畅通无阻.但是很大部分人都还 ...

  6. spring配置c3p0连接池、spring的声明式事务管理

    一.spring配置c3p0连接池: 1.导入maven依赖: <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> & ...

  7. spring的annotation-driven配置事务管理器详解

    来源:http://blog.sina.com.cn/s/blog_8f61307b0100ynfb.html 这篇文章是我从ITeye上复制来的,看了一遍,觉得很深刻,决定把他复制来,对原作者表示感 ...

  8. Spring基于Annotation实现事务管理

    在 Spring 中,除了使用基于 XML 的方式可以实现声明式事务管理以外,还可以通过 Annotation 注解的方式实现声明式事务管理. 使用 Annotation 的方式非常简单,只需要在项目 ...

  9. Spring框架声明式事务管理

    Spring框架声明式事务管理 底层就是AOP原理,面向切面编程 在不修改源码的情况下,对方法进行增强 Spring框架事务管理相关类和API PlatformTransactionManager 平 ...

  10. Spring 注解方式实现 事务管理

    2019独角兽企业重金招聘Python工程师标准>>> 使用步骤: 步骤一.在spring配置文件中引入<tx:>命名空间 <beans xmlns="h ...

最新文章

  1. Linux那些事儿 之 戏说USB(1)它从哪里来
  2. 计算机网络管理的常用命令,网络管理常用命令图文详解.pdf
  3. 训练Rainbow算法需要1425个GPU Day?谷歌说强化学习可以降低计算成本
  4. 20175316 盛茂淞 实验一 Java开发环境的熟悉
  5. 升级php_wamp怎么升级php版本
  6. php 自带sql防注入函数,php 最简单sql防注入函数与方法_PHP教程
  7. 小米与格力的10亿豪赌!
  8. 需求决定设计,设计来源于需求
  9. matlab区分卷积和相关
  10. SPOJ HIGH Highways ——Matrix-Tree定理 高斯消元
  11. mysql建立从库同时备份_mysql主从库配置读写分离以及备份
  12. 2 HTML中的body和它的默认样式
  13. linux切换到管理员失败解决方法(因为误操作导致不能切换到管理员用户)
  14. 尝试对知乎网验证码进行处理:
  15. 如何直观的看出主题模型学习结果的好坏
  16. linux python指向python3_linux下切换python2和python3(转)
  17. 缓存服务的更新策略有哪些?
  18. JavaScript MVC 框架开源软件
  19. 文件服务器php源码,php 在服务器上载文件
  20. 好玩的Deep Dream模型

热门文章

  1. linux 下 c++ clock 函数理解
  2. ORB_SLAM安装问题error: ‘std::chrono::monotonic_clock’ has not been declared
  3. 目标检测 nms非极大抑制算法
  4. tushare 新功能(导入股票和大盘历史数据)
  5. 28. Leetcode 25. K 个一组翻转链表 (链表-反转链表)
  6. 调整[0,x)区间上出现的概率
  7. pytorch笔记 torch.clamp(截取上下限)
  8. MATLAB实战系列(三)- 如何将MATLAB直接转成C/C++代码
  9. 今日话题:坚持真的有用吗?
  10. 【Python刷题】_5