money相关知识
-
Kent Beck 的《测试驱动开发》(TDD) Money示例Ruby版花了一天时间,边看这个Money例子,边把这个java写的Money示例改成了Ruby,只是代码上加了注释,可以按书上的介绍来看。总结一下:TDD方面,感觉确实是很好的开发方法。这种开发方法应该一直贯穿下去。Ruby方面,途中对Ruby的多态(duck type)有了更深的了解。但是也碰到一些问题,没有列出来。解决了再说。测试代码:test/test_dollar.rbCODE:$: .unshift File.join(File.dirname(__FILE__),"..","lib")require'test/unit'require'dollar'class TestDollar < Test::Unit::TestCase#测试数值对象的相等性。这里需要在dollar.rb里重载==方法。#因为比较值大小,ruby和java不同,ruby重载的是==方法,而不是equals.#再添加一行测试,应用三角法。def testE
-
Kent Beck 的《测试驱动开发》(TDD) Money示例Ruby版花了一天时间,边看这个Money例子,边把这个java写的Money示例改成了Ruby,只是代码上加了注释,可以按书上的介绍来看。总结一下:TDD方面,感觉确实是很好的开发方法。这种开发方法应该一直贯穿下去。Ruby方面,途中对Ruby的多态(duck type)有了更深的了解。但是也碰到一些问题,没有列出来。解决了再说。测试代码:test/test_dollar.rbCODE:$: .unshift File.join(File.dirname(__FILE__),"..","lib")require'test/unit'require'dollar'class TestDollar < Test::Unit::TestCase#测试数值对象的相等性。这里需要在dollar.rb里重载==方法。#因为比较值大小,ruby和java不同,ruby重载的是==方法,而不是equals.#再添加一行测试,应用三角法。def testE
-
菜鸟写的答答租车系统父类Car.java package 答答租车; public abstract class Car { public String xuhao; public String name; public int money; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } public Car(){}; public Car(String xuhao,String name,int money){ this.xuhao=xuhao; this.name=n
-
答答租车系统项目~~~~~import java.util.*; //载客车的抽象父类 abstract class Zkc{ public abstract int money(int days); } //奥迪A4载客车 class AudiA4 extends Zkc{ String name = "奥迪A4"; int price = 500; int p_number = 4; int money; public int money(int days){ money = price*days; return money; } } //马自达6载客车 class MaZiDa6 extends Zkc{ String name = "马自达6"; int price = 400; int p_number = 4; int money; public in
money相关课程
money相关教程
- 5. 场景案例 import java.util.Random;import java.util.concurrent.Exchanger;public class ExchangerTest { // 创建一个 Exchanger 对象 private static Exchanger<Object> exchanger = new Exchanger(); public static void main(String[] args) throws InterruptedException { // 模拟快递员 new Thread(() -> { System.out.println( Thread.currentThread().getName() + "送货上门中..."); // 模拟快递送货用时 try { Thread.sleep(new Random().nextInt(10000)); } catch (Exception e) {} System.out.println( Thread.currentThread().getName() + "货物已经送到,等待客户付款"); // 进行货款交换 try { Object money = exchanger.exchange("快递件"); // 收到货款 System.out.println("已经收到货款" + money + ",继续下一单派送..."); } catch (Exception e) {} }, "快递员").start(); // 模拟客户 new Thread(() -> { System.out.println( Thread.currentThread().getName() + "工作中..."); // 模拟工作中用时 try { Thread.sleep(new Random().nextInt(10000)); } catch (Exception e) {} System.out.println( Thread.currentThread().getName() + "接到快递员取件电话,货物已经送到"); try { // 进行货款交换 Object packagz = exchanger.exchange("1000元"); // 收到货款 System.out.println("已经收到货物" + packagz + "..."); } catch (Exception e) {} }, "客户").start(); }}运行上面代码,我们观察一下运行结果。快递员送货上门中...客户工作中...快递员货物已经送到,等待客户付款客户接到快递员取件电话,货物已经送到已经收到货物快递件...已经收到货款1000元,继续下一单派送...观察结果,和我们的预期一致。
- 3. 使用 Object.defineProperty 设置 setter/getter 利用 defineProperty 方法也可以设置 setter/getter。var person = { _cash: 1, _deposit: 99999,};Object.defineProperty(person, 'money', { get: function() { return this._cash + this._deposit; },});Object.defineProperty(person, 'cash', { set: function(val) { console.log('现金发生了改变'); this._cash = val; },});person.cash = 2;console.log(person.money);
- 2.2 工程搭建 1. 创建工程2. 引入依赖<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- Spring jdbc 使用的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies>3. 准备代码实体类代码/** * 账户的实体类 */public class Account implements Serializable { //数据id private Integer id; //账号编码 private String accountNum; //账号金额 private Float money;}持久层接口代码/** * 账户的持久层接口 */public interface IAccountDao { /** * 根据Id查询账户 * @param accountId * @return */ Account findAccountById(Integer accountId); /** * 保存账户 * @param account */ void saveAccount(Account account); /** * 更新账户 * @param account */ void updateAccount(Account account);}持久层实现类代码/** * 账户的持久层实现类 */@Repositorypublic class AccountDaoImpl implements IAccountDao { //jdbc模板类属性 @Autowired private JdbcTemplate jdbcTemplate; //根据id查找 public Account findAccountById(Integer accountId) { List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId); return accounts.isEmpty()?null:accounts.get(0); } public void saveAccount(Account account) { jdbcTemplate.update("insert into account values(?,?,?)", account.getId(),account.getAccountNum(),account.getMoney()); } public void updateAccount(Account account) { jdbcTemplate.update("update account set accountnum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId()); }}业务层接口代码/** * @Auther: wyan */public interface UserService { /** * 账户转账 * @param fromId toId */ public void transMoney(Integer fromId, Integer toId, Integer money);}业务层实现类代码/** * @Auther: wyan * @Description: */@Servicepublic class UserServiceImpl implements UserService { @Autowired private IAccountDao accountDao; public void transMoney(Integer fromId, Integer toId, Integer money) { Account fromAccount = accountDao.findAccountById(fromId); Account toAccount = accountDao.findAccountById(toId); //原始账号减钱 fromAccount.setMoney(fromAccount.getMoney()-money); accountDao.updateAccount(fromAccount); //抛出异常 int i=1/0; //转账账号加钱 toAccount.setMoney(toAccount.getMoney()+money); accountDao.updateAccount(toAccount); }}Tips: 我们再给原始账号减掉钱后,执行保存。然后在这里会出现个异常,就是为了测试事务的特性,所以手动加了个除 0 的代码。4. 配置文件<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///transmoney"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--包路径扫描--> <context:component-scan base-package="com.offcn"></context:component-scan> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--事务的通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="del*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="find*" read-only="true"/> </tx:attributes> </tx:advice> <!--切面的配置--> <aop:config> <aop:pointcut id="pt" expression="execution(* com.offcn.service.*.*(..))"/> <aop:advisor pointcut-ref="pt" advice-ref="txAdvice" /> </aop:config></beans>此处需要注意: context:component-scan:扫描的节点路径为包含 service 和 dao 两个子目录的父级目录; transactionManager: 此节点作用就是初始化 Spring 框架提供的事务管理器的实现类; tx:advice: 此节点的作用是配置切面的通知,因为之前我们的切面类是自定义的,这里使用的是 Spring 提供的事务管理器类作为切面,那么针对什么方法需要做增强呢,在此节点配置,可以看得出来:以 save、del、update 开头的方法都会支持事务,而 find 开头的方法,指定的是只读。 aop:config: 此节点就是 AOP 的相关配置节点了,将切入点和通知整合到一起,同以前的项目差别不大。这里可以看到:切入点规则是针对 service 下面的所有类所有方法任意参数做增强。通知使用的就是我们上面配置过的 tx:advice 节点。5. 测试代码public class AccountServiceTest { public static void main(String[] args) { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.获取业务对象 UserService userService = ac.getBean(UserService.class); //3.从id为1的账号转成1000到2账号 userService.transMoney(1,2,1000); System.out.println("转账完成.."); }}6. 测试结果:ok, 大家,控制台如愿以偿打印了异常的堆栈信息,但是这个不是目的,哈哈哈。目的是在程序执行发生异常的情况下,数据的数据不会错乱。我们可以看见数据库数据并没有发生改变。
- 2.2 代码实现 1. 创建 maven 工程:pom 文件的 jar 包坐标如下:<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency></dependencies>2. 连接数据库的工具类:@Componentpublic class ConnectionUtils { private ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); @Autowired private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * 获取当前线程上的连接 * @return */ public Connection getThreadConnection() { try{ //1.先从ThreadLocal上获取 Connection conn = tl.get(); //2.判断当前线程上是否有连接 if (conn == null) { //3.从数据源中获取一个连接,并且存入ThreadLocal中 conn = dataSource.getConnection(); conn.setAutoCommit(false); tl.set(conn); } //4.返回当前线程上的连接 return conn; }catch (Exception e){ throw new RuntimeException(e); } } /** * 把连接和线程解绑 */ public void removeConnection(){ tl.remove(); }}3. 实体类 Account:public class Account implements Serializable { //数据id private Integer id; //账号编码 private String accountNum; //账号金额 private Float money; //省略get 和set 方法}4. 持久层 dao 和 dao 的 实现类://dao的接口public interface IAccountDao { /** * 更新 * @param account */ void updateAccount(Account account); /** * 根据编号查询账户 */ Account findAccountByNum(String accountNum);}//dao的实现类@Repositorypublic class AccountDaoImpl implements IAccountDao { //dbutil的查询工具类 @Autowired private QueryRunner runner; //连接的工具类 @Autowired private ConnectionUtils connectionUtils; public void setRunner(QueryRunner runner) { this.runner = runner; } public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } //修改账号 public void updateAccount(Account account) { try{ runner.update(connectionUtils.getThreadConnection(),"update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId()); }catch (Exception e) { throw new RuntimeException(e); } } //根据账号查询 public Account findAccountByNum(String accountNum) { try{ List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum); if(accounts == null || accounts.size() == 0){ return null; } if(accounts.size() > 1){ throw new RuntimeException("结果集不唯一,数据有问题"); } return accounts.get(0); }catch (Exception e) { throw new RuntimeException(e); } }}代码解释: AccountDaoImpl 类上面的注解 @Repository 表示使用注解实例化此类,并交给 Spring 的容器管理。5. 业务类 Service 和 Service 的实现类://业务接口public interface IAccountService { /** * 转账 * @param sourceAccount 转出账户名称 * @param targetAccount 转入账户名称 * @param money 转账金额 */ void transfer(String sourceAccount, String targetAccount, Integer money);}//业务实现类@Servicepublic class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public void transfer(String sourceAccount, String targetAccount, Integer money) { Account source = accountDao.findAccountByNum(sourceAccount); Account target = accountDao.findAccountByNum(targetAccount); source.setMoney(source.getMoney()-money); target.setMoney(target.getMoney()+money); accountDao.updateAccount(source); accountDao.updateAccount(target); System.out.println("转账完毕"); }}代码解释:AccountServiceImpl 类上面的注解 @Service 表示使用注解实例化此类,并交给 Spring 的容器管理。6. 事务管理器类@Component@Aspectpublic class TransactionManager { @Autowired private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } @Pointcut("execution(* com.offcn.service.impl.*.*(..))") private void pt1() {} /** * 开启事务 */ @Before("pt1()") public void beginTransaction(){ try { System.out.println("开启事务"); connectionUtils.getThreadConnection().setAutoCommit(false); }catch (Exception e){ e.printStackTrace(); } } /** * 提交事务 */ @AfterReturning("pt1()") public void commit(){ try { System.out.println("提交事务"); connectionUtils.getThreadConnection().commit(); }catch (Exception e){ e.printStackTrace(); } } /** * 回滚事务 */ @AfterThrowing("pt1()") public void rollback(){ try { System.out.println("回滚事务"); connectionUtils.getThreadConnection().rollback(); }catch (Exception e){ e.printStackTrace(); } } /** * 释放连接 */ @After("pt1()") public void release(){ try { System.out.println("释放连接"); connectionUtils.getThreadConnection().close();//还回连接池中 connectionUtils.removeConnection(); }catch (Exception e){ e.printStackTrace(); } }}代码解释:此类通过注解 @Componet 实例化,并且交由 Spring 容器管理,@Aspect 表明它是一个切面类。而下面的注解 @Pointcut 和其余的方法上的各个通知注解,在上面也已经介绍过,这里不做赘述了。主要专注点在于每个注解的通知方法内部引入切入点的表达式方式。7. 配置文件:<?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:aop="http://www.springframework.org/schema/aop" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean> <!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--连接数据库的必备信息--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transmoney"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 注解扫描工程下的包路径--> <context:component-scan base-package="com.offcn"></context:component-scan> <!-- 注解代理模式 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>配置文件说明:dataSource: 采用 c3p0 数据源,大家一定要注意数据库的名称与账号名和密码;queryRunner: dbutils 第三方框架提供用于执行 sql 语句,操作数据库的一个工具类;context:component-scan: 此注解表示注解方式初始化容器扫描的包路径;aop:aspectj-autoproxy: 此注解表示开启代理模式8. 测试类代码@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:applicationContext.xml")public class AccountServiceTest { @Autowired private IAccountService accountService; @Test public void testTransfer(){ accountService.transfer("622200009999","622200001111",100); }}测试结果:执行代码后结果:可以看到,我们通过注解方式配置 Spring 的 AOP 相关配置,同样实现了对于数据的操作。
- 2.1 转账工程的搭建 我们模拟一个实际生活中常见的情景,就是账号的转账。 假设有两个用户 A 和 用户 B,我们通过程序,从 A 账号中转成指定的 money 到 B 账号中。那么,针对正常和异常的程序执行,我们来分析下问题以及它的解决方案。2.1.1 工程准备创建 maven 工程引入 pom 文件的依赖 jar 包坐标信息<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency></dependencies>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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置Service --> <bean id="accountService" class="com.offcn.service.impl.AccountServiceImpl"> <!-- 注入dao --> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置Dao对象--> <bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl"> <!-- 注入QueryRunner --> <property name="runner" ref="queryRunner"></property> <!-- 注入ConnectionUtils --> <property name="connectionUtils" ref="connectionUtils"></property> </bean> <!--配置QueryRunner--> <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"></bean> <!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--连接数据库的必备信息--> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/transmoney"></property> <property name="user" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 配置Connection的工具类 ConnectionUtils --> <bean id="connectionUtils" class="com.offcn.utils.ConnectionUtils"> <!-- 注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean></beans>配置文件说明:connectionUtils 是获取数据库连接的工具类;dataSource 采用 c3p0 数据源,大家一定要注意数据库的名称与账号名和密码;queryRunner 是 dbutils 第三方框架提供用于执行 SQL 语句,操作数据库的一个工具类;accountDao 和 accountService 是我们自定义的业务层实现类和持久层实现类。项目使用数据库环境CREATE TABLE account (id int(11) NOT NULL auto_increment,accountNum varchar(20) default NULL,money int(8) default NULL,PRIMARY KEY (id)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf82.1.2 代码编写实体类代码public class Account implements Serializable { //数据id private Integer id; //账号编码 private String accountNum; //账号金额 private Float money; //省略 get 和 set 的方法}持久层接口//接口代码public interface IAccountDao { /** * 更新 * @param account */ void updateAccount(Account account); /** * 根据编号查询账户 * @param accountNum * @return 如果有唯一的一个结果就返回,如果没有结果就返回null * 如果结果集超过一个就抛异常 */ Account findAccountByNum(String accountNum);}持久层实现类public class AccountDaoImpl implements IAccountDao { //数据库查询工具类 private QueryRunner runner; //数据库连接工具类 private ConnectionUtils connectionUtils; //省略 get 和 set 的方法 //修改账号的方法 public void updateAccount(Account account) { try{ runner.update(connectionUtils.getThreadConnection(), "update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId()); }catch (Exception e) { throw new RuntimeException(e); } } //根据账号查询 Account 对象的方法 public Account findAccountByNum(String accountNum) { try{ List<Account> accounts = runner.query(connectionUtils.getThreadConnection(), "select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum); if(accounts == null || accounts.size() == 0){ return null; } if(accounts.size() > 1){ throw new RuntimeException("结果集不唯一,数据有问题"); } return accounts.get(0); }catch (Exception e) { throw new RuntimeException(e); } }}业务层接口public interface IAccountService { /** * 转账 * @param sourceAccount 转出账户名称 * @param targetAccount 转入账户名称 * @param money 转账金额 */ void transfer(String sourceAccount, String targetAccount, Integer money);}业务层实现类public class AccountServiceImpl implements IAccountService { //持久层对象 private IAccountDao accountDao; //省略 set 和 get 方法 //转账的方法 public void transfer(String sourceAccount, String targetAccount, Integer money) { //查询原始账户 Account source = accountDao.findAccountByNum(sourceAccount); //查询目标账户 Account target = accountDao.findAccountByNum(targetAccount); //原始账号减钱 source.setMoney(source.getMoney()-money); //目标账号加钱 target.setMoney(target.getMoney()+money); //更新原始账号 accountDao.updateAccount(source); //更新目标账号 accountDao.updateAccount(target); System.out.println("转账完毕"); }}测试运行类代码@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:bean.xml")public class AccountServiceTest { @Autowired @Qualifier("proxyAccountService") private IAccountService as; @Test public void testTransfer(){ as.transfer("622200009999","622200001111",100); }}测试结果代码执行完毕,可以看到输出打印转账 ok 了。那么数据库的数据有没有改变呢?我们再看一眼:可以看到:两个账号的数据已经发生了改变,证明转账的动作,确实完成了。那这样看来,我们的代码也没有问题啊,代理模式有什么用呢?接下来我们改造下工程,模拟程序发生异常时候,执行以后的结果如何。2.1.3 改造业务类代码在业务层的代码加入一行异常代码,看看结果是否还会转账成功呢?执行结果:当然了,其实提前也能想得到,肯定会执行失败的啦,哈哈哈哈,我们手动加了运算会出现异常的代码嘛!但是转账的动作是不是也失败了呢?我们再来看一下数据库:问题来了: id 为 1 的账号 money 的列值由原来的 900 变成了 800,说明存款确实减少了 100,但是由于在代码执行的过程中,出现了异常,导致原始账号减少 100 的金钱后保存成功, 而 id 为 2 的账号并没有增加 100。这就出现了数据的事务问题,破坏了数据的原子性和一致性。那么如何解决呢? 思路就是将我们的数据操作代码,使用事务控制起来。由于本小节篇幅有限,我们留待下一小节解决。
- 2.1 工程搭建 1. 创建工程2. 引入依赖<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- Spring jdbc 使用的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies>3. 准备代码实体类代码/** * 账户的实体类 */public class Account implements Serializable { //数据id private Integer id; //账号编码 private String accountNum; //账号金额 private Float money;}持久层接口代码/** * 账户的持久层接口 */public interface IAccountDao { /** * 根据Id查询账户 * @param accountId * @return */ Account findAccountById(Integer accountId); /** * 保存账户 * @param account */ void saveAccount(Account account); /** * 更新账户 * @param account */ void updateAccount(Account account);}持久层实现类代码/** * 账户的持久层实现类 */@Repositorypublic class AccountDaoImpl implements IAccountDao { //jdbc模板类属性 @Autowired private JdbcTemplate jdbcTemplate; //根据id查找 public Account findAccountById(Integer accountId) { List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId); return accounts.isEmpty()?null:accounts.get(0); } public void saveAccount(Account account) { jdbcTemplate.update("insert into account values(?,?,?)", account.getId(),account.getAccountNum(),account.getMoney()); } public void updateAccount(Account account) { jdbcTemplate.update("update account set accountnum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId()); }}业务层接口代码/** * @Auther: wyan */public interface UserService { /** * 账户转账 * @param fromId toId */ public void transMoney(Integer fromId, Integer toId, Integer money);}业务层实现类代码/** * @Auther: wyan * @Description: */@Service@Transactionalpublic class UserServiceImpl implements UserService { @Autowired private IAccountDao accountDao; public void transMoney(Integer fromId, Integer toId, Integer money) { Account fromAccount = accountDao.findAccountById(fromId); Account toAccount = accountDao.findAccountById(toId); //原始账号减钱 fromAccount.setMoney(fromAccount.getMoney()-money); accountDao.updateAccount(fromAccount); //抛出异常 int i=1/0; //转账账号加钱 toAccount.setMoney(toAccount.getMoney()+money); accountDao.updateAccount(toAccount); }}Tips: 此时需要注意注解 @Transactional 的含义。Transactional 就是表示事务,那么在此类上面加入注解,说明需要 Spring 框架针对此类的方法做事务的增强行为,也就是说此注解其实是相当于我们在配置文件中配置的节点 tx:advice。那么这时候有的细心的同学可能会有些疑问:我们在 xml 文件中可以配置事务的传播行为与隔离级别,那么这一个注解如何制定事务的传播行为与隔离级别呢?一个类中如果定义方法过多,而实际上需要增强控制事务的方法只有一部分,如何缩小粒度,只控制需要事务的方法呢?ok,大家。这里有必要跟大家解释下此注解的其余使用方式:问题一答疑:在注解后面可以通过括号内的参数设置隔离级别与传播行为。比如:@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED) 此表达式的含义是事务一定需要,并且是读已提交。问题二答疑:在方法上使用注解。类上面可以不使用 @Transactional 注解,而是将注解写在需要用到事务的方法之上。4. 配置文件<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///transmoney"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--包路径扫描--> <context:component-scan base-package="com.offcn"></context:component-scan> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///transmoney"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--包路径扫描--> <context:component-scan base-package="com.offcn"></context:component-scan> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--注解事务驱动--> <tx:annotation-driven/></beans>Tips: 此处需要注意 tx:annotation-driven 节点无需配置通知节点与切面节点,而是使用 tx:annotation-driven 节点表示,事务的支持方式为声明式事务。5. 测试代码public class AccountServiceTest { public static void main(String[] args) { //1.获取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.获取业务对象 UserService userService = ac.getBean(UserService.class); //3.从id为1的账号转成1000到2账号 userService.transMoney(1,2,1000); System.out.println("转账完成.."); }}6. 测试结果:ok, 大家,我们继续测试之前的转账代码,依然得到错误的异常信息。同时数据库的金额并没有发生改变,因为事务的控制,保证了数据的一致性原子性。那么也证明我们声明式事务的案例测试成功。
money相关搜索
-
mac osx
machine_start
macox
magellan
malloc
manifest
manifest文件
map
map 遍历
mapreduce编程
maps google com
margin
margin bottom
margin left
margin right
margin top
marginbottom
marginheight
marginleft
margintop