热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring+mybatis+mysql使用事物的几种方法总结

这篇文章主要给大家总结介绍了关于Spring+mybatis+mysql使用事物的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

本文主要记录下spring是如何支持事物的,以及在Spring结合mybatis时,可以怎么简单的实现数据库的事物功能,下面话不多说了,来一起看看详细的介绍吧。

I. 前提

case1:两张表的的事物支持情况

首先准备两张表,一个user表,一个story表,结构如下

CREATE TABLE `user` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
 `pwd` varchar(26) NOT NULL DEFAULT '' COMMENT '密码',
 `isDeleted` tinyint(1) NOT NULL DEFAULT '0',
 `created` varchar(13) NOT NULL DEFAULT '0',
 `updated` varchar(13) NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`),
 KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `story` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `userId` int(20) unsigned NOT NULL DEFAULT '0' COMMENT '作者的userID',
 `name` varchar(20) NOT NULL DEFAULT '' COMMENT '作者名',
 `title` varchar(26) NOT NULL DEFAULT '' COMMENT '密码',
 `story` text COMMENT '故事内容',
 `isDeleted` tinyint(1) NOT NULL DEFAULT '0',
 `created` varchar(13) NOT NULL DEFAULT '0',
 `updated` varchar(13) NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`),
 KEY `userId` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

我们的事物场景在于用户修改name时,要求两张表的name都需要一起修改,不允许出现不一致的情况

case2:单表的事物支持

转账,一个用户减钱,另一个用户加钱

CREATE TABLE `money` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
 `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
 `isDeleted` tinyint(1) NOT NULL DEFAULT '0',
 `created` varchar(13) NOT NULL DEFAULT '0',
 `updated` varchar(13) NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`),
 KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

相比上面那个case,这个更加简单了,下面的实例则主要根据这个进行说明,至于case1,则留待扩展里面进行

首先是实现对应的dao和entity

@Data
public class MoneyEntity implements Serializable {
 private static final long serialVersiOnUID= -7074788842783160025L;
 private int id;
 private String name;
 private int money;
 private int isDeleted;
 private int created;
 private int updated;
}

public interface MoneyDao {
 MoneyEntity queryMoney(@Param("id") int userId);
 // 加钱,负数时表示减钱
 int incrementMoney(@Param("id") int userId, @Param("addMoney") int addMoney);
}

对应的mapper文件为

<&#63;xml version="1.0" encoding="UTF-8"&#63;>


 
 id, `name`, `money`, `isDeleted`, `created`, `updated`
 

 

 
 update money
 set mOney=money + #{addMoney}
 where id=#{id}
 

对应的mybatis连接数据源的相关配置


 
 classpath*:jdbc.properties
 




 
 
 
 

 

 
 
 
 

 
 

 
 
 
 

 
 




 
 
 





 

II. 实例演示

通过网上查询,Spring事物管理总共有四种方式,下面逐一进行演示,每种方式是怎么玩的,然后看实际项目中应该如何抉择

1. 硬编码方式

编程式事物管理,既通过TransactionTemplate来实现多个db操作的事物管理

a. 实现

那么,我们的转账case可以如下实现

@Repository
public class CodeDemo1 {
 @Autowired
 private MoneyDao moneyDao;
 @Autowired
 private TransactionTemplate transactionTemplate;
 /**
 * 转账
 *
 * @param inUserId
 * @param outUserId
 * @param payMoney
 * @param status 0 表示正常转账, 1 表示内部抛出一个异常, 2 表示新开一个线程,修改inUserId的钱 +200, 3 表示新开一个线程,修改outUserId的钱 + 200
 */
 public void transfor(final int inUserId, final int outUserId, final int payMoney, final int status) {
 transactionTemplate.execute(new TransactionCallbackWithoutResult() {
 protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
 MoneyEntity entity = moneyDao.queryMoney(outUserId);
 if (entity.getMoney() > payMoney) { // 可以转账

 // 先减钱
 moneyDao.incrementMoney(outUserId, -payMoney);

 
 testCase(inUserId, outUserId, status);

 // 再加钱
 moneyDao.incrementMoney(inUserId, payMoney);
 System.out.println("转账完成! now: " + System.currentTimeMillis());
 }
 }
 });
 }
 
 
 // 下面都是测试用例相关
 private void testCase(final int inUserId, final int outUserId, final int status) {
 if (status == 1) {
 throw new IllegalArgumentException("转账异常!!!");
 } else if(status == 2) {
 addMoney(inUserId);
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 } else if (status == 3) {
 addMoney(outUserId);
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }


 public void addMoney(final int userId) {
 System.out.printf("内部加钱: " + System.currentTimeMillis());
 new Thread(new Runnable() {
 public void run() {
 moneyDao.incrementMoney(userId, 200);
 System.out.println(" sub modify success! now: " + System.currentTimeMillis());
 }
 }).start();
 }
}

主要看上面的transfor方法,内部通过 transactionTemplate 来实现事物的封装,内部有三个db操作,一个查询,两个更新,具体分析后面说明

上面的代码比较简单了,唯一需要关注的就是transactionTemplate这个bean如何定义的,xml文件中与前面重复的就不贴了,直接贴上关键代码, 一个是根据DataSource创建的TransactionManager,一个则是根据TransactionManager创建的TransactionTemplate



 



 

b. 测试用例

正常演示情况, 演示没有任何异常,不考虑并发的情况

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:spring/service.xml", "classpath*:test-datasource1.xml"})
public class CodeDemo1Test {
 @Autowired
 private CodeDemo1 codeDemo1;

 @Autowired
 private MoneyDao moneyDao;

 @Test
 public void testTransfor() {

 System.out.println("---------before----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());


 codeDemo1.transfor(1, 2, 10, 0);

 System.out.println("---------after----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());
 }
}

输出如下,两个账号的钱都没有问题

---------before----------
id: 1 mOney= 10000
id: 2 mOney= 50000
转账完成! now: 1526130394266
---------after----------
id: 1 mOney= 10010
id: 2 mOney= 49990

转账过程中出现异常,特别是转账方钱已扣,收款方还没收到钱时,也就是case中的status为1的场景

// 内部抛异常的情况
@Test
public void testTransforException() {

 System.out.println("---------before----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());


 try {
 codeDemo1.transfor(1, 2, 10, 1);
 } catch (Exception e) {
 e.printStackTrace();
 }

 System.out.println("---------after----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());
}

对此,我们希望把转账方的钱还回去, 输出如下,发现两个的钱都没有变化

---------before----------
id: 1 mOney= 10010
id: 2 mOney= 49990
---------after----------
id: 1 mOney= 10010
java.lang.IllegalArgumentException: 转账异常!!!
 ... // 省略异常信息
id: 2 mOney= 49990

当status为2,表示在转账人钱已扣,收款人钱没收到之间,又有人给收款人转了200,此时根据mysql的锁机制,另外人的转账应该是立马到的(因为收款人账号没有被锁住),且金额不应该有问题

输出结果如下:

---------before----------
id: 1 mOney= 10010
id: 2 mOney= 49990
## 右边是注释: 转账过程中,另外存钱立马到账,没有被锁住
内部加钱: 1526130827480
sub modify success! now: 1526130827500
## 存钱结束
转账完成! now: 1526130830488
---------after----------
id: 1 mOney= 10220
id: 2 mOney= 49980

当status为3, 表示在转账人钱已扣,收款人钱没收到之间,又有人给转账人转了200,这时因为转账人的记录以及被加了写锁,因此只能等待转账的事物提交之后,才有可能+200成功,当然最终的金额也得一致

输出结果如下

---------before----------
id: 1 mOney= 10220
id: 2 mOney= 49980
## 右边是注释:内部存钱了,但没有马上成功
## 直到转账完成后,才立马存成功,注意两个时间戳
内部加钱: 1526131101046
转账完成! now: 1526131104051
sub modify success! now: 1526131104053
---------after----------
id: 1 mOney= 10230
id: 2 mOney= 50170

c. 小结

至此,编程式事物已经实例演示ok,从上面的过程,给人的感觉就和直接写事物相关的sql一样,

start transaction;

-- 这中间就是 TransactionTemplate#execute 方法内部的逻辑
-- 也就是需要事物管理的一组sql

commit;

2. 基于TransactionProxyFactoryBean方式

接下来的三个就是声明式事物管理,这种用得也比较少,因为需要每个事物管理类,添加一个TransactionProxyFactoryBean

a. 实现

除了将 TransactionTemplate 干掉,并将内部的sql逻辑移除之外,对比前面的,发现基本上没有太多差别

public class FactoryBeanDemo2 {
 @Autowired
 private MoneyDao moneyDao;
 /**
 * 转账
 *
 * @param inUserId
 * @param outUserId
 * @param payMoney
 * @param status 0 表示正常转账, 1 表示内部抛出一个异常, 2 表示新开一个线程,修改inUserId的钱 +200, 3 表示新开一个线程,修改outUserId的钱 + 200
 */
 public void transfor(final int inUserId, final int outUserId, final int payMoney, final int status) {

 MoneyEntity entity = moneyDao.queryMoney(outUserId);
 if (entity.getMoney() > payMoney) { // 可以转账

 // 先减钱
 moneyDao.incrementMoney(outUserId, -payMoney);


 testCase(inUserId, outUserId, status);


 // 再加钱
 moneyDao.incrementMoney(inUserId, payMoney);
 System.out.println("转账完成! now: " + System.currentTimeMillis());
 }


 }


 private void testCase(final int inUserId, final int outUserId, final int status) {
 if (status == 1) {
 throw new IllegalArgumentException("转账异常!!!");
 } else if (status == 2) {
 addMoney(inUserId);
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 } else if (status == 3) {
 addMoney(outUserId);
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }


 public void addMoney(final int userId) {
 System.out.println("内部加钱: " + System.currentTimeMillis());
 new Thread(new Runnable() {
 public void run() {
 moneyDao.incrementMoney(userId, 200);
 System.out.println("sub modify success! now: " + System.currentTimeMillis());
 }
 }).start();
 }
}

重点来了,主要是需要配置一个 TransactionProxyBeanFactory,我们知道BeanFactory就是我们自己来创建Bean的一种手段,相关的xml配置如下



 






 
 
 
 
 
 
 
 
 
 PROPAGATION_REQUIRED
 
 
 
 

通过上面的配置,大致可以了解到这个通过TransactionProxyFactoryBean就是创建了一个FactoryBeanDemo2的代理类,这个代理类内部封装好事物相关的逻辑,可以看做是前面编程式的一种简单通用抽象

b. 测试

测试代码与前面基本相同,唯一的区别就是我们使用的应该是上面BeanFactory生成的Bean,而不是直接使用FactoryBeanDemo2

正常演示case:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:spring/service.xml", "classpath*:test-datasource2.xml"})
public class FactoryBeanDemo1Test {
 @Resource(name = "factoryBeanDemoProxy")
 private FactoryBeanDemo2 factoryBeanDemo2;
 @Autowired
 private MoneyDao moneyDao;

 @Test
 public void testTransfor() {
 System.out.println("---------before----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());
 factoryBeanDemo2.transfor(1, 2, 10, 0);
 System.out.println("---------after----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());
 }
}

输出

---------before----------
id: 1 mOney= 10000
id: 2 mOney= 50000
转账完成! now: 1526132058886
---------after----------
id: 1 mOney= 10010
id: 2 mOney= 49990

status为1,内部异常的情况下,我们希望钱也不会有问题

@Test
public void testTransforException() {
 System.out.println("---------before----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());

 try {
 factoryBeanDemo2.transfor(1, 2, 10, 1);
 } catch (Exception e) {
 System.out.println(e.getMessage());;
 }

 System.out.println("---------after----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());
}

输出为

---------before----------
id: 1 mOney= 10010
id: 2 mOney= 49990
转账异常!!!
---------after----------
id: 1 mOney= 10010
id: 2 mOney= 49990

status为2 时,分析结果与上面应该相同,输出如下

---------before----------
id: 1 mOney= 10010
id: 2 mOney= 49950
内部加钱: 1526133325376
sub modify success! now: 1526133325387
转账完成! now: 1526133328381
---------after----------
id: 1 mOney= 10220
id: 2 mOney= 49940

status为3时,输出

---------before----------
id: 1 mOney= 10220
id: 2 mOney= 49940
内部加钱: 1526133373466
转账完成! now: 1526133376476
sub modify success! now: 1526133376480
---------after----------
id: 1 mOney= 10230
id: 2 mOney= 50130

c. 小结

TransactionProxyFactoryBean 的思路就是利用代理模式来实现事物管理,生成一个代理类,拦截目标方法,将一组sql的操作封装到事物中进行;相比较于硬编码,无侵入,而且支持灵活的配置方式

缺点也显而易见,每个都要进行配置,比较繁琐

3. xml使用方式

Spring有两大特点,IoC和AOP,对于事物这种情况而言,我们可不可以使用AOP来做呢?

对于需要开启事物的方法,拦截掉,执行前开始事物,执行完毕之后提交事物,出现异常时回滚

这样一看,感觉还是蛮有希望的,而下面两种姿势正是这么玩的,因此需要加上aspect的依赖


 org.aspectj
 aspectjweaver
 1.8.7

a. 实现

java类与第二种完全一致,变动的只有xml


xmlns:tx="http://www.springframework.org/schema/tx" 
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="...
 http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd"



 
 
 
 





 
 
 
 

观察上面的配置,再想想第二种方式,思路都差不多了,但是这种方式明显更加通用,通过切面和切点,可以减少大量的配置

b. 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:spring/service.xml", "classpath*:test-datasource3.xml"})
public class XmlBeanTest {
 @Autowired
 private XmlDemo3 xmlDemo;

 @Autowired
 private MoneyDao moneyDao;


 @Test
 public void testTransfor() {

 System.out.println("---------before----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());


 xmlDemo.transfor(1, 2, 10, 0);

 System.out.println("---------after----------");
 System.out.println("id: 1 mOney= " + moneyDao.queryMoney(1).getMoney());
 System.out.println("id: 2 mOney= " + moneyDao.queryMoney(2).getMoney());
 }
}

这个测试起来,和一般的写法就没啥两样了,比第二种的FactoryBean的注入方式简单点

正常输出

---------before----------
id: 1 mOney= 10000
id: 2 mOney= 50000
转账完成! now: 1526135301273
---------after----------
id: 1 mOney= 10010
id: 2 mOney= 49990

status=1 出现异常时,输出

---------before----------
id: 1 mOney= 10010
id: 2 mOney= 49990
转账异常!!!
---------after----------
id: 1 mOney= 10010
id: 2 mOney= 49990

status=2 转账过程中,又存钱的场景,输出,与前面预期一致

---------before----------
id: 1 mOney= 10010
id: 2 mOney= 49990
内部加钱: 1526135438403
sub modify success! now: 1526135438421
转账完成! now: 1526135441410
---------after----------
id: 1 mOney= 10220
id: 2 mOney= 49980

status=3 的输出,与前面预期一致

---------before----------
id: 1 mOney= 10220
id: 2 mOney= 49980
内部加钱: 1526135464341
转账完成! now: 1526135467349
sub modify success! now: 1526135467352
---------after----------
id: 1 mOney= 10230
id: 2 mOney= 50170

4. 注解方式

这个就是消灭xml,用注解来做的方式,就是将前面xml中的配置用 @Transactional注解替换

a. 实现

@Repository
public class AnnoDemo4 {
 @Autowired
 private MoneyDao moneyDao;
 /**
 * 转账
 *
 * @param inUserId
 * @param outUserId
 * @param payMoney
 * @param status 0 表示正常转账, 1 表示内部抛出一个异常, 2 表示新开一个线程,修改inUserId的钱 +200, 3 表示新开一个线程,修改outUserId的钱 + 200
 *
 *
 * Transactional注解中的的属性 propagation :事务的传播行为 isolation :事务的隔离级别 readOnly :只读
 * rollbackFor :发生哪些异常回滚 noRollbackFor :发生哪些异常不回滚
 * rollbackForClassName 根据异常类名回滚
 */
 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly= false)
 public void transfor(final int inUserId, final int outUserId, final int payMoney, final int status) {
 MoneyEntity entity = moneyDao.queryMoney(outUserId);
 if (entity.getMoney() > payMoney) { // 可以转账
 // 先减钱
 moneyDao.incrementMoney(outUserId, -payMoney);
 testCase(inUserId, outUserId, status);
 // 再加钱
 moneyDao.incrementMoney(inUserId, payMoney);
 System.out.println("转账完成! now: " + System.currentTimeMillis());
 }
 }

 private void testCase(final int inUserId, final int outUserId, final int status) {
 if (status == 1) {
 throw new IllegalArgumentException("转账异常!!!");
 } else if (status == 2) {
 addMoney(inUserId);
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 } else if (status == 3) {
 addMoney(outUserId);
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }

 private void addMoney(final int userId) {
 System.out.println("内部加钱: " + System.currentTimeMillis());
 new Thread(new Runnable() {
 public void run() {
 moneyDao.incrementMoney(userId, 200);
 System.out.println("sub modify success! now: " + System.currentTimeMillis());
 }
 }).start();
 }
}

因此需要在xml中配置,开启事物注解



 


这样一看,就更加清晰了,实际项目中,xml和注解方式也是用得最多的场景了

b. 测试case

和第三种测试case完全相同, 输出结果也一样,直接省略

III. 小结

上面说了Spring中四种使用事物的姿势,其中硬编码方式可能是最好理解的,就相当于将我们写sql中,使用事物的方式直接翻译成对应的java代码了;而FactoryBean方式相当于特殊情况特殊对待,为每个事物来一个代理类来增强事物功能;后面的两个则原理差不多都是利用事物通知(AOP)来实现,定义切点及相关信息

编程式:

  • 注入 TransactionTemplate
  • 将利用事物的逻辑封装到 transactionTemplate#execute方法内

代理BeanFactory:

  • 利用 TransactionProxyFactoryBean 为事物相关类生成代理
  • 使用方通过FactoryBean获取代理类,作为使用的Bean

xml配置:

  • 利用 tx标签 + aop方式来实现
  • 标签定义事物通知,内部可有较多的配置信息
  • 配置切点,切面

注解方式:

  • 在开启事物的方法or类上添加 @Transactional 注解即可
  • 开启事物注解 <tx:annotation-driven transaction-manager="transactionManager"/>

IV. 其他

1. 参考

文档

Spring事务管理的四种方式

源码

  • 项目源码:study-demo  (本地下载)
  • 主要查看包路径: 事物demo (本地下载)
  • 测试相关代码: 测试demo (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • PHP组合工具以及开发所需的工具
    本文介绍了PHP开发中常用的组合工具和开发所需的工具。对于数据分析软件,包括Excel、hihidata、SPSS、SAS、MARLAB、Eview以及各种BI与报表工具等。同时还介绍了PHP开发所需的PHP MySQL Apache集成环境,包括推荐的AppServ等版本。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • mysql-cluster集群sql节点高可用keepalived的故障处理过程
    本文描述了mysql-cluster集群sql节点高可用keepalived的故障处理过程,包括故障发生时间、故障描述、故障分析等内容。根据keepalived的日志分析,发现bogus VRRP packet received on eth0 !!!等错误信息,进而导致vip地址失效,使得mysql-cluster的api无法访问。针对这个问题,本文提供了相应的解决方案。 ... [详细]
  • ubuntu用sqoop将数据从hive导入mysql时,命令: ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
author-avatar
倒叙的时光_SX_126
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有