Spring + Jta +JDBCTemplate 分布式事物实现方式
来源:互联网 发布:福建天正网络咨询 编辑:程序博客网 时间:2024/05/18 04:58
最近项目中需要用到多数据源管,数据访问层采用的是JDBCTemplate去做的,一开始是在数据源这块做了一个多数据源的操作类,通过拦截器注解去动态赋值指定的数据源操作,这种做法在查询中是没有问题的,但是DML操作时,会出现问题:事物中无法动态操作数据源,导致很多操作指针对第一个库。查询资料的时候发现:
DataSourceTransactionManager这个事物管理类只针对单个数据源进行事物控制.
解决的方案也有多种,这里只提及我实践过的两种:
- 开启多个数据源去进行操作,这种是可以自己去实现的.
- 利用JTA和Atomikos的多数据源分布事物管理
方案一:
思路:
- 自定义一个注解类,通过传递参数告诉这个业务需要用到哪几个数据源
- 然后仿照Spring中的@Transactional的实现模式,去构建一个集合来开启多个事物
- 然后通过拦截器去动态分配业务给这个集合告诉他,要开几个事物
代码:
applicationContext-datasource.xml
<!-- 数据源1 --> <bean id="mvp" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="${mvp.driverClassName}"></property> <property name="url" value="${mvp.url}"></property> <property name="username" value="${mvp.username}"></property> <property name="password" value="${mvp.password}"></property> <property name="filters" value="${mvp.filters}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${mvp.initialSize}"/> <property name="minIdle" value="${mvp.minIdle}"/> <property name="maxActive" value="${mvp.maxActive}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${mvp.maxWait}"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${mvp.timeBetweenEvictionRunsMillis}"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${mvp.minEvictableIdleTimeMillis}"/> <property name="testWhileIdle" value="${mvp.testWhileIdle}"/> <property name="testOnBorrow" value="${mvp.testOnBorrow}"/> <property name="testOnReturn" value="${mvp.testOnReturn}"/> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="${mvp.poolPreparedStatements}"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <property name="removeAbandoned" value="${mvp.removeAbandoned}" /> <property name="removeAbandonedTimeout" value="${mvp.removeAbandonedTimeout}" /> <property name="maxOpenPreparedStatements" value="${mvp.maxOpenPreparedStatements}" /> <property name="logAbandoned" value="${mvp.logAbandoned}"/> <property name="queryTimeout" value="${mvp.querytimeout}"/> <property name="validationQuery" value="${mvp.validationQuery}"/> </bean> <!-- 数据源2 --> <bean id="system" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="${system.driverClassName}"></property> <property name="url" value="${system.url}"></property> <property name="username" value="${system.username}"></property> <property name="password" value="${system.password}"></property> <property name="filters" value="${system.filters}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${system.initialSize}"/> <property name="minIdle" value="${system.minIdle}"/> <property name="maxActive" value="${system.maxActive}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${system.maxWait}"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${system.timeBetweenEvictionRunsMillis}"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${system.minEvictableIdleTimeMillis}"/> <property name="testWhileIdle" value="${system.testWhileIdle}"/> <property name="testOnBorrow" value="${system.testOnBorrow}"/> <property name="testOnReturn" value="${system.testOnReturn}"/> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="${system.poolPreparedStatements}"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <property name="removeAbandoned" value="${system.removeAbandoned}" /> <property name="removeAbandonedTimeout" value="${system.removeAbandonedTimeout}" /> <property name="maxOpenPreparedStatements" value="${system.maxOpenPreparedStatements}" /> <property name="logAbandoned" value="${system.logAbandoned}"/> <property name="queryTimeout" value="${system.querytimeout}"/> <property name="validationQuery" value="${system.validationQuery}"/> </bean> <!-- dao层访问处理工具 分别指定两个不同的datasource --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="default"/> </bean> <bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="system"/> </bean>
applicationContext-service.xml 业务层配置文件
<!-- 一些扫描包或者其他定义的业务类 我这边就不列入了 --><!-- 多数据源切面类 --> <bean id="multiTransactionalAspect" class="com.elab.execute.utils.MultiTransactionalAspect"/> 多数据Aop配置 <aop:config proxy-target-class="true"> <!-- 定义一个切入点表达式: 拦截哪些方法 --> <aop:aspect ref="multiTransactionalAspect"> <aop:pointcut id="pointUserMgr" expression="execution(* com.test.execute.services..*(..))"/> <aop:around method="around" pointcut-ref="pointUserMgr" /> </aop:aspect> </aop:config>
注解类 :
@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface MultiTransactional { String[] values() default ""; // TODO 当然你如果想做的更完善一点,可以参考@Transactional这个类,自己去做,什么传播机制啊,隔离级别啊 等等,思路是一样的}
注解实现类 - 其实这里差不多都是沿用Spring的事物实现方式:
import com.alibaba.druid.pool.DruidDataSource;import org.apache.commons.lang3.ArrayUtils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.stereotype.Component;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.DefaultTransactionDefinition;import java.lang.reflect.Method;import java.util.Stack;/** * @Description: 多数据源动态管理 * @Author: Liukx on 2017/7/28 - 16:41 */@Component("multiTransactionalAspect")public class MultiTransactionalAspect { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private SpringUtils springUtils; /** * 切入点 * * @param point * @return * @throws Throwable */ public Object around(ProceedingJoinPoint point) throws Throwable { Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>(); Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>(); try { Object target = point.getTarget(); String method = point.getSignature().getName(); Class classz = target.getClass(); Class[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes(); Method m = classz.getMethod(method, parameterTypes); if (m != null && m.isAnnotationPresent(MultiTransactional.class)) { MultiTransactional multiTransactional = m.getAnnotation(MultiTransactional.class); if (!openTransaction(dataSourceTransactionManagerStack, transactionStatuStack, multiTransactional)) { return null; } Object ret = point.proceed(); commit(dataSourceTransactionManagerStack, transactionStatuStack); return ret; } Object ret = point.proceed(); return ret; } catch (Exception e) { rollback(dataSourceTransactionManagerStack, transactionStatuStack); logger.error(String.format( "MultiTransactionalAspect, method:%s-%s occors error:", point .getTarget().getClass().getSimpleName(), point .getSignature().getName()), e); throw e; } } /** * 打开一个事物方法 * * @param dataSourceTransactionManagerStack * @param transactionStatuStack * @param multiTransactional * @return */ private boolean openTransaction( Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack, Stack<TransactionStatus> transactionStatuStack, MultiTransactional multiTransactional) { // 获取注解中要打开的事物类型 String[] transactionMangerNames = multiTransactional.values(); if (ArrayUtils.isEmpty(multiTransactional.values())) { return false; } for (String beanName : transactionMangerNames) { // 创建一个新的事物管理器,用来管理接下来要用到的事物 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); // 根据注解中获取到的数据标识,从spring容器中去查找对应的数据源 DruidDataSource dataSource = (DruidDataSource) springUtils.getBean(beanName); //然后交给事物管理器去管理 dataSourceTransactionManager.setDataSource(dataSource); // 定义一个新的事物定义 DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition(); // 设置一个默认的事物传播机制,注意的是这里可以拓展注解中没有用到的属性 // defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(defaultTransactionDefinition); // 将这个事物的定义放入Stack中 /** * 其中为什么要用Stack来保存TransactionManager和TransactionStatus呢? * 那是因为Spring的事务处理是按照LIFO/stack behavior的方式进行的。 * 如若顺序有误,则会报错: */ transactionStatuStack.push(transactionStatus); dataSourceTransactionManagerStack .push(dataSourceTransactionManager); } return true; } /** * 提交事物方法实现 * * @param dataSourceTransactionManagerStack * @param transactionStatuStack */ private void commit( Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack, Stack<TransactionStatus> transactionStatuStack) { while (!dataSourceTransactionManagerStack.isEmpty()) { dataSourceTransactionManagerStack.pop().commit( transactionStatuStack.pop()); } } /** * 回滚事物方法实现 * * @param dataSourceTransactionManagerStack * @param transactionStatuStack */ private void rollback( Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack, Stack<TransactionStatus> transactionStatuStack) { while (!dataSourceTransactionManagerStack.isEmpty()) { dataSourceTransactionManagerStack.pop().rollback( transactionStatuStack.pop()); } }}
service 1 - 2 :
/** * 一些实验性Demo接口 * * @author Liukx * @create 2017-07-28 10:11 * @email liukx@elab-plus.com **/public interface IDemoService { public void mvpManage() throws Exception;}/** * 一些实验性Demo接口 * * @author Liukx * @create 2017-07-28 10:11 * @email liukx@elab-plus.com **/public interface IDemoService2 { public void beidouMange() throws CoreException;}
service实现类:
/** * 实现性Demo接口实现 * * @author Liukx * @create 2017-07-28 10:12 * @email liukx@elab-plus.com **/@Service("demoService")public class DemoServiceImpl implements IDemoService { @Autowired @Qualifier("demoTestDao") private IDemoTestDao testDao; @Autowired private IDemoService2 demoService2;// @Autowired// private DataSourceTransactionManager transactionManager; //TODO 这个注解很关键 - 必须是这个注解才能被拦截器拦截到 @MultiTransactional(values = {DataSource.mvp, DataSource.system}) public void mvpManage() throws CoreException { System.out.println("=====================开始处理MVP"); testDao.insert(); List<Map<String, Object>> select = testDao.select(); System.out.println("===============>>>>>>>>>>>>>>>>>" + select.size()); System.out.println("=====================结束处理MVP"); demoService2.beidouMange();// System.out.println("业务处理完成,开始报错"); int i = 1 / 0; }}
service 2实现
/** * @author Liukx * @create 2017-07-28 11:07 * @email liukx@elab-plus.com **/@Service("demo2")public class DemoServiceImpl2 implements IDemoService2 { @Autowired @Qualifier("beidouTestDao") private IDemoTestDao testDao; @MultiTransactional(values = DataSource.system) public void beidouMange() throws CoreException { System.out.println("=====================开始处理北斗"); testDao.insert(); List<Map<String, Object>> select = testDao.select(); System.out.println("========================================>" + select.size()); System.out.println("=====================结束处理北斗"); }}
dao实现: 这里就只列入实现类
@Repository("beidouTestDao")public class BeiDouTestDaoImpl extends BaseDao implements IDemoTestDao { /** * 我们这块目前是有一些封装的,不过你不用太关注; * 你只要把对应的数据源指定好就行了 */ @Autowired @Qualifier("jdbcTemplate2") public JdbcTemplate jdbcTemplate; public int insert() throws CoreException { LinkedHashMap<String, Object> params = new LinkedHashMap<>(); params.put("name", "某某某" + RandomUtils.randomNum(3)); params.put("created", new Date()); return executeInsert("test.insert", params); } @Override public List<Map<String, Object>> select() throws CoreException { return findList("test.findAll", new LinkedHashMap<String, Object>()); }}
@Repository("demoTestDao")public class DemoTestDaoImpl extends BaseDao implements IDemoTestDao { /** * 我们这块目前是有一些封装的,不过你不用太关注; * 你只要把对应的数据源指定好就行了 */ @Autowired @Qualifier("jdbcTemplate") public JdbcTemplate jdbcTemplate; public int insert() throws CoreException { LinkedHashMap<String, Object> params = new LinkedHashMap<>(); params.put("name", "某某某" + RandomUtils.randomNum(2)); params.put("created", new Date()); return executeInsert("test.insert", params); } public List<Map<String, Object>> select() throws CoreException { return findList("test.findAll", new LinkedHashMap<String, Object>()); }}
另外有一个动态获取bean的工具类:
@Componentpublic class SpringUtils implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public Object getBean(String beanId) { return applicationContext.getBean(beanId); }}
PS : 这个方案会存在一定的问题 -> 就是如果你下一个另一个数据库操作的业务serivce方法中如果定义了@Transactional它将会又开启一个事物去执行…
方案2 : 利用JTA+Atomikios实现事物管理,业务层代码和上面差不多,主要是配置文件这块.
applicationContext-datasource.xml
<!-- 父配置 --><bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true"> <property name="xaDataSourceClassName" value="com.alibaba.druid.pool.xa.DruidXADataSource"/> <!-- SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana] --> <property name="poolSize" value="10"/> <property name="minPoolSize" value="10"/> <property name="maxPoolSize" value="30"/> <property name="borrowConnectionTimeout" value="60"/> <property name="reapTimeout" value="20"/> <property name="maxIdleTime" value="60"/> <property name="maintenanceInterval" value="60"/> <property name="loginTimeout" value="60"/> <property name="testQuery" value="${default.validationQuery}"/> </bean> <!-- 主配置 --> <bean id="masterDataSource" parent="abstractXADataSource"> <property name="uniqueResourceName" value="masterDB"/> <property name="xaProperties"> <props> <prop key="driverClassName">${default.driverClassName}</prop> <prop key="url">${default.url}</prop> <prop key="password">${default.password}</prop> <!-- <prop key="user">${jdbc.username}</prop> --> <!-- mysql --> <prop key="username">${default.username}</prop> <!-- durid --> <prop key="initialSize">0</prop> <prop key="maxActive">20</prop> <!-- 若不配置则代码执行"{dataSource-1} inited"此处停止 --> <prop key="minIdle">0</prop> <prop key="maxWait">60000</prop> <prop key="validationQuery">${default.validationQuery}</prop> <prop key="testOnBorrow">false</prop> <prop key="testOnReturn">false</prop> <prop key="testWhileIdle">true</prop> <prop key="removeAbandoned">true</prop> <prop key="removeAbandonedTimeout">1800</prop> <prop key="logAbandoned">true</prop> <prop key="filters">mergeStat</prop> </props> </property> </bean> <bean id="slaveDataSource" parent="abstractXADataSource"> <property name="uniqueResourceName" value="slaveDB"/> <property name="xaProperties"> <props> <prop key="driverClassName">${system.driverClassName}</prop> <prop key="url">${system.url}</prop> <prop key="password">${system.password}</prop> <!-- <prop key="user">${jdbc.username}</prop> --> <prop key="username">${system.username}</prop> <prop key="initialSize">0</prop> <prop key="maxActive">20</prop> <prop key="minIdle">0</prop> <prop key="maxWait">60000</prop> <prop key="validationQuery">${system.validationQuery}</prop> <prop key="testOnBorrow">false</prop> <prop key="testOnReturn">false</prop> <prop key="testWhileIdle">true</prop> <prop key="removeAbandoned">true</prop> <prop key="removeAbandonedTimeout">1800</prop> <prop key="logAbandoned">true</prop> <prop key="filters">mergeStat</prop> </props> </property> </bean> <!-- 将数据源交给数据库操作模版工具 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="masterDataSource"/> </bean> <bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="slaveDataSource"/> </bean>
applicationContext-atomikos.xml
<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"> <description>配置事物</description> <!-- atomikos事务管理器 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown"> <value>true</value> </property> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300"/> </bean> <!-- spring 事务管理器 --> <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager"/> <property name="userTransaction" ref="atomikosUserTransaction"/> <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default --> <property name="allowCustomIsolationLevels" value="true"/> </bean> <aop:config proxy-target-class="true"> <aop:pointcut id="pointUserMgr" expression="execution(* com.elab.execute.services..*(..))"/> <aop:advisor pointcut-ref="pointUserMgr" advice-ref="txAdvice"/> </aop:config> <!-- 通过切入点去实现事物触发机制 --> <tx:advice id="txAdvice" transaction-manager="springTransactionManager"> <tx:attributes> <tx:method name="get*" propagation="REQUIRED" read-only="true"/> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="has*" propagation="REQUIRED" read-only="true"/> <tx:method name="locate*" propagation="REQUIRED" read-only="true"/> <tx:method name="mvp*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="beidou*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> </tx:attributes> </tx:advice> <!--<tx:annotation-driven transaction-manager="springTransactionManager"--> <!--proxy-target-class="true"></tx:annotation-driven>--></beans>
pom.xml
<!-- JTA --> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-api</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.2</version> </dependency>
业务层代码和方案一的差不多,看了一下大概的思路:
1. 在调用被拦截器匹配的方法时,开启一个新的事物
2. 当执行到DML操作时,会获取他对应的数据源,并且会和当前线程的事物管理器中的数据源进行匹配,如果不存在,则到连接池中获取一个新的连接,并把这个连接放入当前线程的事物管理器中进行管理
3. 当所有业务执行完毕,并且没有报错的时候,会执行一个两阶段提交的方式
PREPARE TRANSACTION transaction_id PREPARE TRANSACTION 为当前事务的两阶段提交做准备。 在命令之后,事务就不再和当前会话关联了;它的状态完全保存在磁盘上, 它提交成功有非常高的可能性,即使是在请求提交之前数据库发生了崩溃也如此。这条命令必须在一个用BEGIN显式开始的事务块里面使用。 COMMIT PREPARED transaction_id 提交已进入准备阶段的ID为transaction_id的事务 ROLLBACK PREPARED transaction_id 回滚已进入准备阶段的ID为transaction_id的事务
推荐使用第二种方式,因为它有比较好的连接池以及相对完善的机制,第一种考虑的情况比较少,会出现问题,当然,你如果愿意折腾自己写一套,可以参考一下..
以上为个人学习参考,有什么不足的地方欢迎指正.
参考博客:
Mybatis + JTA http://blog.csdn.net/zmx729618/article/details/54344296
分布式事物提交以及JTA的概念 : http://www.jasongj.com/big_data/two_phase_commit/
JTA一些实现原理:https://www.ibm.com/developerworks/cn/java/j-lo-jta/
- Spring + Jta +JDBCTemplate 分布式事物实现方式
- spring jta 分布式事物配置
- Spring分布式事务- 三种实现方式(Spring+JTA)
- Spring分布式事务- 三种实现方式(Spring+JTA)
- spring+jta实现全局事物管理
- spring实现分布式事物
- Spring+JTA实现分布式事务
- sqlserver JTA 分布式事物支持
- spring集成jotm实现JTA分布式事务管理
- Spring JTA 分布式事务
- Spring JTA 分布式事务
- 分布式事物的spring的实现
- spring+mybatis+atomikos实现分布式事物管理
- Spring实现分布式事务JTA(使用atomiko实现)
- Spring实现分布式事务JTA(使用atomiko实现)
- spring使用编程的方式进行事物处理_普通方式和jdbctemplate方式
- Postgresql 分布式事务JTA实现Atomikos与Spring集成实践
- (十二)Spring Boot+Druid+Mybatis实现JTA分布式事务
- IE下按钮超链接无法跳转
- 如何判断一个整数x是否可以表示成n个连续正整数的和
- <算法>基于三路划分的链表快速排序
- 最小二乘回归树Python实现——统计学习方法第五章课后题
- 圆与多边形求交面积
- Spring + Jta +JDBCTemplate 分布式事物实现方式
- 第一次用CSDN的MarkDown写博客,遇到个奇葩问题!
- 常见类---String类
- iOS开发 数据存储之SQLite3
- Fast.Plans.v10.1.WinALL 1CD(室内设计绘图软件)
- PS方式配置FPGA和在線升級
- Java实现文件目录的遍历
- 随机森林简介
- Effective and Efficient Malware Detection at the End Host