Spring注解开发

1. IoC的综合案例(CRUD) - 纯xml开发

1.1 综合案例介绍

案例的需求

实现账户表的增删改查操作

案例的要求

选用基于XML的Spring和Mybatis整合配置实现。

  • 数据库表结构介绍

CREATE TABLE `account` (
`id`  int(11) NOT NULL ,
`name`  varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money`  double NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT

1.2 案例的实现

1.2.1 创建工程导入坐标

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.20</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

1.2.2 编写基础代码

/**
 * 账户的实体类
 */
public class Account {
​
    private Integer id;
    private String name;
    private Double money;
​
    //省略set,get,toString等方法
}
/**
 * 账户的业务层接口
 */
public interface AccountService {
    /**
     * 保存
     */
    void save(Account account);
​
    /**
     * 根据id删除
     */
    void delete(Integer id);
​
    /**
     * 更新账户
     */
    void update(Account account);
​
    /**
     * 根据id查询
     */
    Account findById(Integer id);
​
    /**
     * 根据名称查询账户
     */
    Account findByName(String name);
​
    /**
     * 查询所有
     */
    List<Account> findAll();
}
​
/**
 * 账户业务接口实现类
 */
public class AccountServiceImpl implements AccountService {
​
    private AccountDao accountDao;
​
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
​
    @Override
    public void save(Account account) {
        accountDao.save(account);
    }
​
    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }
​
    @Override
    public void update(Account account) {
        accountDao.update(account);
    }
​
    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }
​
    @Override
    public Account findByName(String name) {
        return accountDao.findByName(name);
    }
​
    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }
}
​
/**
 * 账户持久层接口
 */
public interface AccountDao {
    /**
     * 保存
     */
    void save(Account account);
​
    /**
     * 根据id删除
     */
    void delete(Integer id);
​
    /**
     * 更新账户
     */
    void update(Account account);
​
    /**
     * 根据id查询
     */
    Account findById(Integer id);
​
    /**
     * 根据名称查询账户
     */
    Account findByName(String name);
​
    /**
     * 查询所有
     */
    List<Account> findAll();
}

1.2.3 编写mybatis的映射配置

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hou.dao.AccountDao">
​
    <!--保存-->
    <insert id="save" parameterType="account">
        insert into account values(#{id},#{name},#{money})
    </insert>
​
    <!--根据id删除-->
    <delete id="delete" parameterType="int" >
        delete from account where id=#{id}
    </delete>
​
    <!--更新账户-->
    <update id="update" parameterType="account">
        update account set name=#{name},money=#{money} where id=#{id}
    </update>
​
    <!--根据id查询-->
    <select id="findById" parameterType="int" resultType="account">
        select * from account where id=#{id}
    </select>
​
    <!--根据名称查询账户-->
    <select id="findByName" parameterType="string" resultType="account">
        select * from account where name=#{name}
    </select>
​
    <!--查询所有-->
    <select id="findAll" resultType="account">
        select * from account
    </select>
</mapper>

1.2.4 创建Spring配置文件并导入约束

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">    
</beans>

1.2.5 编写spring和mybatis整合配置

<!--配置dao-->
<!--配置properties文件的位置-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置mybatis的SqlSessionFactory工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="druidDataSource"></property>
    <property name="typeAliasesPackage" value="com.hou.pojo"></property>
</bean>
<!--配置创建dao代理实现类的扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.hou.dao"></property>
</bean>

1.2.6 编写spring的配置

<!--配置service-->
<bean id="accountService" class="com.hou.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>

1.2.7 测试

/*
测试类
 */
public class CRUDTest {
    @Test
    public void findByName(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        AccountService accountService = applicationContext.getBean(AccountService.class);
        Account account = accountService.findByName("迪丽热巴");
        System.out.println("account = " + account);
    }
​
    @Test
    public void findAll(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        AccountService accountService = applicationContext.getBean(AccountService.class);
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

 

2. Spring注解开发

2.1 bean标签和注解的对应

  • bean标签对应注解@Component

    • 注解属性value:bean标签的id属性

    • 不指定value属性,默认就是类名,首字母小写

    • 该注解衍生出了三个注解,@Controller,@Service,@Repository,用法和@Componet一致,为了更加清晰的提现层的概念。

  • bean标签属性scope对应注解@Scope

    • 注解属性value:singleton,prototype

  • bean标签属性init-method对应注解@PostConstruct

  • bean标签属性destroy-method对应注解@PreDestroy

  • service层

    public interface AccountService {
    ​
        //模拟保存账户
        void save();
    }
    //@Component("accountService")
    @Service("accountService")
    @Scope("prototype")
    public class AccountServiceImpl implements AccountService {
    ​
        private AccountDao accountDao;
    ​
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    ​
        @Override
        public void saveAccount() {
            accountDao.saveAccount();
        }
    }
  • dao层

    public interface AccountDao {
    ​
        //模拟保存账户
        void save();
    }
    //@Component("accountDao")
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Override
        public void saveAccount() {
            System.out.println("保存了账户");
        }
    ​
        private void init() {
            System.out.println("AccountDao对象初始化");
        }
    ​
        private void destroy() {
            System.out.println("AccountDao对象 销毁了");
        }
    }
  • 添加applicationContext配置文件命名空间

    <?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"
           xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd"
    >
        
        <!-- 开启spring的注解扫描,扫描包中类的注解-->
        <context:component-scan base-package="com.hou"></context:component-scan>
    </beans>
  • 测试注解

    @Test
    public void testIOC(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = context.getBean(AccountService.class);
        AccountDao accountDao = context.getBean(AccountDao.class);
    ​
        System.out.println(accountService);
        System.out.println(accountDao);
    ​
        context.close();
    }

2.2 依赖注入注解

  • @Autowired注解(Spring框架提供)

    按照类型注入,如果无法确定唯一类型(接口有多个实现类),需要配合注解@Qualifier的使用

  • @Qualifier("id") 注解(Spring框架提供)

    • 按照id注入

  • @Resource注解(JDK提供)

    • 注解属性name:配置类的id

    @Resource(name="accountDao")
    private AccountDao accountDao;

代码演示

  • 业务层

@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements AccountService {
​
     @Autowired
    //@Qualifier("accountDao2")
    //@Resource(name = "accountDao2")
    private AccountDao accountDao;
​
    @Override
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }
}
  • dao层

package com.hou.dao.impl;
​
/**
 * 账户dao实现类
 */
@Component("accountDao")
//@Repository("accountDao")
@Scope("singleton")
public class AccountDaoImpl implements AccountDao {
​
    @Override
    public void saveAccount(Account account) {
        System.out.println("模拟转账");
    }
​
    @PostConstruct()
    public void init(){
        System.out.println("AccountDaoImpl 初始化...");
    }
​
    @PreDestroy
    public void destroy(){
        System.out.println("AccountDaoImpl 销毁...");
    }
}
​

 

2.3 Spring对Junit的支持

  • junit运行的时候底层使用了Runner对象,有一个默认使用的Runner对象。

  • Spring对junit的支持,其实是自己实现了一个Runner对象(按照junit runner的要求实现)

  • Spring对junit的支持的体现

    • 好处一:配置完之后,不需要我们手动的启动Spring

    • 好处二:可以在junit测试类中使用@AutoWired等方式注入对象,直接对其进行调用测试

  • 使用步骤

    • 引入spring-test.jar

    • 配置测试类

  //Spring框架中的Runner对象, 替换Junit中的Runner对象
  @RunWith(SpringJUnit4ClassRunner.class)
​
  //框架启动入口, xml配置文件启动(2选1)
  @ContextConfiguration(locations = "classpath:beans.xml")
  //框架启动入口, 注解方式配置文件启动(2选1)
  //@ContextConfiguration(classes = SpringConfig.class)
  • 代码实现

    • pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>
    • 测试类

    package com.hou;
    ​
    //Spring框架中的Runner对象, 替换Junit中的Runner对象
    @RunWith(SpringJUnit4ClassRunner.class)
    //框架启动入口, 注解方式配置文件启动
    //@ContextConfiguration(classes = SpringConfig.class);
    //框架启动入口, xml配置文件启动
    @ContextConfiguration(locations = "classpath:ApplicationContext.xml")
    public class AccountTest {
    ​
        //注入业务层接口
        @Autowired
        private AccountService service;
    ​
        @Test
        public void testIOC(){
    ​
            System.out.println("service = " + service);
            Account account = new Account();
            account.setName("小米");
            account.setMoney(888.0F);
            service.saveAccount(account);
        }
    }

 

3. IoC的综合案例(CRUD) - 半注解半xml开发

企业主流的开发方式

注意:往往第三方jar中的对象我们使用xml配置(比如druid数据库连接池、Mybatis的SQLSessionFactory),类似于service层和dao层的实现类,这属于我们自己写的代码,往往会使用注解,这就是半xml半注解的模式。

  • applicationContext.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    ​
        <!--配置dao-->
        <!--配置properties文件的位置-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <!--配置数据源-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
        <!--配置mybatis的SqlSessionFactory工厂-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="druidDataSource"></property>
            <property name="typeAliasesPackage" value="com.hou.pojo"></property>
        </bean>
        <!--配置创建dao代理实现类的扫描器-->
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.hou.dao"></property>
        </bean>
    ​
        <!--配置service-->
        <!--让Spring开启注解扫描-->
        <context:component-scan base-package="com.hou"></context:component-scan>
    </beans>
  • service层

    package com.hou.service.impl;
    ​
    /**
     * 账户业务接口实现类
     */
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
    ​
        //依赖注入
        @Autowired
        private AccountDao accountDao;
    ​
        //增删改查方法 无修改, 笔记中代码省略
        
    }
  • 测试类

/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
public class AccountServiceTest {
​
    @Autowired
    private AccountService accountService;
​
    //测试类方法省略
    @Test
    public void findAll() {
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

 

4. IoC的综合案例(CRUD) - 纯注解开发

  • @Configuration标识当前类是Spring的一个配置类

  • @ComponentScan替代xml中的<context:component-scan/>

  • @Import引入其他配置类,被引入的配置类可以不加@Configuration注解

  • @PropertySource:引入外部properties文件,注意加classpath:

  • @Value对成员变量赋值

  • @Bean将一个方法的返回值对象加入到Spring的容器当中管理

  • @Qualifier可以使用在方法参数上,表明对应的形参引入/注入的对象类型

4.1 SpringConfig框架启动配置类

//Spring配置类,框架启动入口
@Configuration
//启动注解扫描
@ComponentScan({"com.hou"})
public class SpringConfig {
​
    //方法的返回值, 加入到SpringIOC容器中管理
    @Bean("druidDataSource")
    public DataSource createDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/ssm_lx");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
        return druidDataSource;
    }
​
    @Bean("sqlSessionFactory")
    public SqlSessionFactoryBean createSqlSessionFactoryBean(@Qualifier("druidDataSource") DataSource ds){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(ds);
        sqlSessionFactoryBean.setTypeAliasesPackage("com.hou.pojo");
        return sqlSessionFactoryBean;
    }
​
    @Bean("scannerConfigurer")
    public MapperScannerConfigurer createMapperScannerConfigurer(){
        MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
        scannerConfigurer.setBasePackage("com.hou.dao");
        return scannerConfigurer;
    }
}
​
​
  • 测试

/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
//框架启动入口, 注解方式配置文件启动(2选1)
@ContextConfiguration(classes = SpringConfig.class)
public class CRUDTest {
​
    @Autowired
    private AccountService accountService;
​
    @Test
    public void findByName(){
        Account account = accountService.findByName("迪丽热巴");
        System.out.println("account = " + account);
    }
​
    @Test
    public void findAll(){
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

 

4.2 注解开发的SpringConfig配置优化

  • SpringConfig框架启动配置类

    /*
    作为Spring框架的主配置文件
     */
    @Configuration
    //开启Spring容器的注解扫描
    @ComponentScan({"com.hou"})
    //导入子配置文件
    @Import({JDBCConfig.class, MybatisConfig.class})
    public class SpringConfig {
    ​
    }
  • JDBCConfig配置类

    //用于指定与数据库相关配置的配置文件
    @Configuration
    @PropertySource({"classpath:jdbc.properties"})
    public class JDBCConfig {
    ​
        @Value("${jdbc.driverClassName}")
        private String driverClassName;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    ​
        //配置数据源
        @Bean("ataSource")
        public DataSource createDataSource(){
            //创建Druid数据源
            DruidDataSource druidDataSource = new DruidDataSource();
            //配置相关信息(Driver, url, username, password)
            druidDataSource.setDriverClassName(driverClassName);
            druidDataSource.setUrl(url);
            druidDataSource.setUsername(username);
            druidDataSource.setPassword(password);
    ​
            return druidDataSource;
        }
    }
  • Mybatis配置类

  //用于配置与Mybatis相关的配置
  @Configuration
  public class MybatisConfig {
      //配置SqlSessionFactoryBean对象
      @Bean("sqlSessionFactory")
      public SqlSessionFactoryBean createSqlSessionFactoryBean(DataSource ds){
          //创建SqlSessionFactoryBean 对象
          SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
          //配置相关信息(数据源, pojo别名映射)
          sqlSessionFactory.setDataSource(ds);
          sqlSessionFactory.setTypeAliasesPackage("com.hou.pojo");
​
          return sqlSessionFactory;
      }
​
      //配置dao的包扫描
      @Bean("scannerConfigurer")
      public MapperScannerConfigurer createMapperScannerConfigurer(){
          //创建MapperScannerConfigurer 对象
          MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
          //配置相关信息(扫描的dao包, 找到了dao层的接口文件, 找到了SQL映射文件, 生成接口实现类对象并存到Spring容器中)
          scannerConfigurer.setBasePackage("com.hou.dao");
​
          return scannerConfigurer;
      }
  }
  • 测试类

    /*
    测试类
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    //加载的xml配置文件
    //@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
    ​
    //加载的注解形式的Spring主配置文件
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountServiceTest {
    ​
        @Autowired
        private AccountService accountService;
        
        //其他方法省略
    ​
        @Test
        public void findAll() {
            List<Account> accountList = accountService.findAll();
            for (Account account : accountList) {
                System.out.println("account = " + account);
            }
        }
    }

5. 案例:模拟转账

案例:模拟转账(并且模拟转账异常)

  • 汇款人账户减少一定的金额

  • 收款人账户增加一定的金额

  • 计算之后,更新数据库

  • 问题:模拟转账异常(人为制造异常,在两次update之间造了异常)

5.1 转账编码

1、引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.20</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
</dependencies>

2、业务层

/**
 * 账户的业务层接口
 */
public interface AccountService {
    /**
     * 转账
     */
    void transfer(String source, String target, double money);
    
    //其他方法省略
}
/**
 * 账户业务接口实现类
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {
​
    //依赖注入
    @Autowired
    private AccountDao accountDao;
​
    /*
    转账业务逻辑
    1.先查询账户信息
    2.修改账户信息
    3.持久化账户信息
     */
    @Override
    public void transfer(String source, String target, double money) {
        try {
            //开启事务
​
            // 转账业务逻辑
            //1 先查询账户信息
            Account sourceAccount = accountDao.findByName(source);
            Account targetAccount = accountDao.findByName(target);
            //2 修改账户信息
            sourceAccount.setMoney(sourceAccount.getMoney() - money);
            targetAccount.setMoney(targetAccount.getMoney() + money);
            //3 持久化账户信息
            accountDao.update(sourceAccount);
            //int i = 1/0;
            accountDao.update(targetAccount);
​
            //提交事务
        } catch (Exception e){
            e.printStackTrace();
            //回滚事务
​
        } finally {
            //关闭资源
        }
    }
    
    //其他方法省略
}

3、测试

/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class CRUDTest {
​
    @Autowired
    private AccountService accountService;
​
    @Test
    public void tranfer(){
        accountService.transfer("迪丽热巴","古力娜扎",1);
    }
}

4、发现问题

转账过程出现事务问题


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值