Java开发工程师(Web方向)

第4章--数据访问

Spring JDBC

DAO (Data Access Object) 实现数据访问相关接口(接口和实现分离)

ORM (Object Relation Mapping) 对象关系映射:数据库中记录<->Java对象

Why Spring JDBC?

之前使用JDBC时:设置连接参数、打开连接、声明SQL语句、执行SQL、得到访问结果、执行程序特定的业务、处理捕获的异常、关闭连接语句和结果集等

Class.forName(JDBC_DRIVER); DriverManager.getConnection(DB_URL, USER, PASS); conn.createStatement(); 等等

真正需要关心的是:设置连接参数、SQL语句以及参数、执行程序特定业务

Spring JDBC:封装用户不需要关心的那些底层实现细节,通过接口暴露给用户,从而提高效率

DataSource:数据源--设置连接参数

包括了:驱动类名、数据库连接地址、用户名、密码

接口方法:getConnection

DateSource不是Spring提供的,而是JavaEE本身就提供的,而Spring提供了它的不同实现

配置:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<context:property-placeholder location="db.properties"/>

jdbcTemplate:数据库SQL语句及参数、异常处理

select -- queryForObject()

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from user", Integer.class);

int countOfNamedJoe = this.jdbcTemplate.queryForObject("select count(*) from user where first_name = ?", Integer.class, "Joe");

int lastName = this.jdbcTemplate.queryForObject("select last_name from user where id = ?", new Object[]{1212L}, String.class);

// queryForObject提供了很多接口,比如例二和例三中parameter的位置不一样

insert/delete/update -- update()

this.jdbcTemplate.update("insert into user (first_name, last_name) values (?, ?)", "Meimei", "Han");

this.jdbcTemplate.update("update user set last_name = ? where id = ?)", "Li", 5276L);

this.jdbcTemplate.update("delete from user where id = ?)", Long.valueOf(userId));

create -- execute()

this.jdbcTemplate.execute("create table user (id int, first_name varchar(100), last_name varchar(100))");

通过RowMapper可以将返回的结果转换为一个个单个的Object对象:

// 返回一个Java对象
User user = this.jdbcTemplate.queryForObject( "select first_name, last_name from user where id = ?", new Object[] {1212L}, new RowMapper<User>() { public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setFirstName(rs.getString("first_name")); user.setLastName(rs.getString("last_name")); return user; } });
// 返回一系列Java对象
List<User> users = this.jdbcTemplate.query(
    "select first_name, last_name from user",
    new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setFirstName(rs.getString("first_name"));
            user.setLastName(rs.getString("last_name"));
            return user;
        }
    });

定义JdbcTemplate:

public class JdbcExampleDao implements ExampleDao {
    private JdbcTemplate jdbcTemplate;
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // ... DAO 接口实现
}

通过xml中的bean配置DAO

<bean id="exampleDao" class="com.netease.course.JdbcExampleDao">
    <property name="dataSource" ref="dataSource"/>
</bean>

通过Annotation配置DAO

@Repository
public class JdbcExampleDao implements ExampleDao {
    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // ... DAO 接口实现
}

// @Repository与Component相似,定义了一个bean并指明了是个DAO的bean

// @Autowired自动注入了setDataSource

使用实例:

1. 创建Maven工程

artifact-id: com.netease.course

group-id: spring-data

2. pom中添加spring容器的依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.netease.course</groupId>
  <artifactId>spring-data</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.2.1.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>4.2.1.RELEASE</version>
      </dependency>
  </dependencies>
  
</project>

3. src/main/resources中创建spring的空的xml配置文件application-context.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
    http://www.springframework.org/schema/context/spring-context.xsd">
    
    
</beans>

4. src/main/resources中创建database的配置文件db.properties

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost/dbname
jdbc.username = matt
jdbc.password = matt 

5. 在application-context.xml中配置数据源

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>
<context:property-placeholder location="db.properties" />

6. src/main/java下创建包com.netease.course

7. 在com.netease.course包中创建DAO文件JdbcTemplateDao.java

@Repository
public class JdbcTemplateDao {
    
    private JdbcTemplate jdbcTemplate;
    
    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    
    public void createTable() {
        jdbcTemplate.execute("create table user (id int primary key, first_name varchar(100), "
                + "last_name varchar(100))");
    }
}

8. 由于是通过Annotation配置的,所以需要在application-context.xml配置文件中说明

<context:component-scan base-package="com.netease.course" />

9. 在com.netease.course包中创建测试类TestData.java

public class TestData {

    public static void main (String[] args) throws Exception {

        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        
        JdbcTemplateDao dao = context.getBean("jdbcTemplateDao", JdbcTemplateDao.class);
        dao.createTable();
        
        ((ConfigurableApplicationContext) context).close();
    }

}

// 得到Annotation定义的Dao实例,并执行Dao中的方法

10. Run

Error:

11. 解决方法:在dependencies里加上dbcp的依赖

<dependency>
      <groupId>commons-dbcp</groupId>
     <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
</dependency>

12. Run

报错

原因:com.mysql.jdbc.Driver就是定义在db.properties中的,需要添加MySQL的依赖

13. 添加MySQL驱动的依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.25</version>
</dependency>

14. Run

15. extends the functionalities

// DAO.java
public void insertData() {
    this.jdbcTemplate.update("insert into example values (1, ?, ?)", "Meimei", "Han");
    this.jdbcTemplate.update("insert into example values (2, ?, ?)", "Lei", "Li");
}
    
public int count() {
    return this.jdbcTemplate.queryForObject("select count(*) from user", Integer.class);
}

// Test.java
dao.insertData();
System.out.println(dao.count());

运行成功,打印2

16. 将返回结果转换为User对象:在com.netease.course包下创建User.java

private int id;
private String firstName;
private String lastName;
// 并写上setters和getters

在JdbcTemplateDao.java中

public List<User> getUserList() {
    return this.jdbcTemplate.query("select * from example", new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setFirstName(rs.getString("first_name"));
            user.setLastName(rs.getString("last_name"));
            return user;
        }
    });
}

Test.java

List<User> users = dao.getUserList();
for (User user: users) {
    System.out.println(user.getId() + ":" + user.getFirstName() + " " + user.getLastName());
}

运行返回:

1:Meimei Han

2:Lei Li

NamedParameterJdbcTemplate:为了解决复杂sql语句中包含过多?导致的混乱

例:

// NamedParameterJdbcTemplateDao.java
@Repository
public class NamedParameterJdbcTemplateDao {
    
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
    
    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    }
    
    public int countOfUsersByFirstName(String firstName) {
        String sql = "select count(*) from example where first_name = :first_name";
        Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
        return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
        // 将map传递给queryForObject()作为参数,namedParameterJdbcTemplate会自动的匹配对应的参数名称的值,并组装成完整的sql
    }
}

// Test.java
NamedParameterJdbcTemplateDao dao = context.getBean("namedParameterJdbcTemplateDao", NamedParameterJdbcTemplateDao.class);
System.out.println(dao.countOfUsersByFirstName("Meimei"));

queryForObject的用法:

queryForObject(String sql, Map<String,?> paramMap, RowMapper<T> rowMapper)

queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType)

可通过SqlParameterSource的子类BeanPropertySqlParameterSource来简化过程

SQLException异常处理:

无法连接到数据库、sql语法错误、row/column不存在等

是一个checked exception:必须使用try-catch或throws解决

但是由于这些SQLException一旦出现,事实上整个程序是无法运行的

且需要不厌其烦地写这些try-catch

--> Spring中使用DataAccessException (是一种unchecked的exception)

--不需到处都写try-check和throws,只需在最终层写上一个try-catch即可

DataAccessException有很多很多细分的子类(在org.springframework.dao包下)

 

事务管理

背景:1. 使用JDBC事务和Hibernate事务的接口不同,导致兼容需要修改代码。

   2. 若有众多事务需要管理,通过代码进行管理事务会十分繁琐、混乱

Spring事务管理:

优点:统一的事务编程模型,兼容不同底层

   编程式事务及声明式事务(AOP)

PlatformTransactionManager:org.springframework.transaction

接口:

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

实现:

DataSourceTransactionManager:基于JDBC的实现 org.springframework.jdbc.datasource

HibernateTransactionManager:基于Hibernate的实现 org.springframework.orm.hibernate*

-- 接口一致

TransactionDefinition:事务的定义

getName:事务名称

getIsolationLevel:隔离级别

getPropagationBehavior:传播行为

getTimeout:超时时间(超过则回滚)

isReadOnly:是否是只读事务

TransactionStatus:事务的状态

isNewTransaction:是否是新的事务

hasSavepoint:是否有savepoint(诊断,NESTED)

isCompleted:是否已完成

isRollbackOnly:事务结果是否是rollback-only(只有rollback一个结果而不会commit)

setRollbackOnly:设置事务为rollback-only(当TransactionTemplate时才会调用)

隔离级别:

ISOLATION_READ_UNCOMMITTED: 读未提交

ISOLATION_READ_COMMITTED: 读提交

ISOLATION_REPEATABLE_READ: 重复读

ISOLATION_SERIALIZABLE: 串行化

ISOLATION_DEFAULT: 默认:根据底层数据库而定

传播行为:跟事务本身属性无关,跟函数调用有关,影响了事务的提交状态

比如两个事务嵌套,事务间是相互影响的呢还是相互不影响的

值:

PROPAGATION_MANDATORY: 函数必须在一个事务中运行,事务不存在则抛异常(比如在调用某函数之前,必须要存在一个事务,否则该函数抛异常)

PROPAGATION_NEVER: 不应该在事务中运行,否则抛异常

PROPAGATION_NOT_SUPPORTED: 不应该在事务中运行,否则把事务挂起

PROPAGATION_SUPPORTS: 不需要事务,但是若有事务了,则在事务中执行

PROPAGATION_REQUIRED: 必须在事务中执行,否则启动新事务

内部事务会影响外部事务:设T1调用了T2,若T2抛出异常,会影响外部的T1,因此T1和T2都要rollback

PROPAGATION_NESTED: 必须在事务中执行,否则启动新事务

事务之间相互不影响:上述T2的异常不会影响T1,只需rollback T2

从底层实现来看,每个logical的transaction都有自己的一个savepoint,失败时回滚到savepoint即可

PROPAGATION_REQUIRES_NEW: 必须在新事务中执行,挂起当前事务(即每个logical事务都对应一个独立的physical事务)

NB: 设代码中的transactionA调用了transactionB,看起来是两个Transaction,但是在底层数据库实现时,很可能把这两个视为一个数据库事务

逻辑事务 logical:比如上述transactionA和B

物理事务 physical:比如上述数据库事务

声明式事务:

在bean中添加配置

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

对应location:

xsi:schemaLocation="

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd"

定义事务管理器:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

定义事务Advice:(事务是基于AOP实现的)

 

<tx:advice id="txAdvice" transaction-manager="txManager">
       <tx:attributes>
       <tx:method name="get*" read-only="true"/>
       <tx:method name="*"/>
       </tx:attributes>
</tx:advice>

 

 

配置<tx:method/>

name:匹配的函数名称(支持*匹配)

propagation:事务传播行为

isolation:事务隔离级别

timeout:超时

read-only:只读

rollback-for:触发回滚的异常,用逗号分隔 

no-rollback-for:不触发回滚的异常(正常提交),用逗号分隔 

本例:

事务Advice应用到get*方法上,是read-only="true"的

事务Advice应用到get*方法上,使用默认隔离方式、默认隔离级别和默认传播行为等

定义Pointcut:(AOP)

<aop:config>
        <aop:pointcut expression="execution(* com.netease.course.AccountDao.*(..))" id="daoOperation"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation"/>
</aop:config>

使用声明式事务,需要定义很多的xml配置

Spring也提供了Annotation的方式来声明:

@Transactional

<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

配置属性:

value:使用哪一个TransactionManager

propagation:事务传播行为

isolation:事务隔离级别

timeout:超时

readOnly:只读

rollbackFor:触发回滚的异常类对象数组(是.class对象)

rollbackForClassName:触发回滚的异常类名称数组(是名称)

noRollbackFor/noRollbackForClassName:不触发回滚

具体函数的Annotation例子:

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public boolean methodName(..) {...}

实例:转账

Account.java

public class Account {
    private String user;
    private double balance;
    
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}

AccountDao.java

@Repository
public class AccountDao {
    private JdbcTemplate jdbcTemplate;
    
    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    
    public void resetMoney() {
        jdbcTemplate.update("update account set balance=1000");
    }
    
    public List<Account> accountList() {
        return this.jdbcTemplate.query("select * from account", new RowMapper<Account>() {
            public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                Account account = new Account();
                account.setUser(rs.getString("user"));
                account.setBalance(rs.getDouble("balance"));
                return account;
            }
        });
    }
    
    public void transferMoney(String source, String target, double amount) {
        this.jdbcTemplate.update("update account set balance=balance-? where user=?", amount, source);
        this.jdbcTemplate.update("update account set balance=balance+? where user=?", amount, target);
    }
}

Test.java

public static void main (String[] args) throws Exception {

    ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        
    AccountDao dao = context.getBean("accountDao", AccountDao.class);

    dao.resetMoney();
    List<Account> accountList = dao.accountList();
    for(Account account: accountList) {
        System.out.println(account.getUser() + ":" + account.getBalance());
    }
        
    dao.transferMoney("Kimi", "Miki", 520);
    accountList = dao.accountList();
    for(Account account: accountList) {
        System.out.println(account.getUser() + ":" + account.getBalance());
    }
        
    ((ConfigurableApplicationContext) context).close();
}

运行:

事务测试:

在dao中添加private void throwException(){ throw new RuntimeException("ERROR"); }

在两次update之间调用throwException();

并在test.java中的transferMoney()周围写上try-catch块,catch中syso出e.getMessage();

运行:

转账操作失败,需要通过事务方式进行设定,全部失败或是全部成功

改进:使用事务来完成这些任务

在application-context.xml中加入schema/tx和schema/aop

在application-context.xml中加入<context:component-scan base-package="com.netease.course" /> 以使用Annotation

在transferMoney()定义加上@Transactional(propagation = Propagation.REQUIRED)

运行结果均为1000.0,正确

编程式事务:

TransactionTemplate:在回调里写相关事务操作即可,不需要关心何时commit何时rollback

PlatformTransactionManager的实现:

 

 

整合MyBatis

数据访问单元测验

本次得分为:11.00/11.00, 本次测试的提交时间为:2017-09-24
1单选(2分)

关于Spring JDBC说法错误的是:

  • A.简化访问数据库的代码,提高效率;
  • B.可以直接帮助我们将数据库记录转化成Java对象;�2.00/2.00
  • C.更方便的异常处理;
  • D.可以帮助我们进行资源管理:连接的创建,关闭等;
2单选(2分)

关于Spring事务管理,说明不正确的是:

  • A.统一的事务管理模型;
  • B.声明式事务支持;
  • C.编程式事务支持;
  • D.不依赖于底层事务的实现;�2.00/2.00
3单选(2分)

假设事务A,B的传播行为定义为PROPAGATION_REQUIRED,由A事务会调用B事务,如果B事务抛出异常,并由A事务中代码捕获,那么A事务是否可以正常提交:

  • A.以上说法都不对;
  • B.不能提交;�2.00/2.00
  • C.可以提交;
  • D.由A事务确定;
4多选(3分)

关于SQLException和DataAccessException,说明错误的是:

  • A.SQLException是Spring提供的异常类;�1.50/3.00
  • B.DataAccessException是checked异常;�1.50/3.00
  • C.DataAccessException是Spring提供的异常类;
  • D.SQLException是checked异常;
5判断(2分)

MyBatis通过Java Annotation写的Mapper必须通过接口(interface)来实现,不能通过类(class)。

  • A.×
  • B.√�2.00/2.00

数据访问单元作业 

http://zhanjingbo.site/14766902757975.html

1(12分)

根据本单介绍的Spring JDBC,事务管理,MyBatis等内容,分别使用Spring JDBC及MyBatis提供一个转帐服务(保证事务),提供一个transferMoney接口:

transferMoney(Long srcUserId, Long targetUserId, double count);// srcUserId及targetUserId为转帐用户标识
转帐涉及到的表(UserBalance)包含如下列:
userId:Long,代表用户标识;
balance:Doubcle,代表帐号余额。

基本要求:必须附加一个项目说明文档,说明每个功能点对应程序的运行结果(截图),项目的接口说明或者关键代码(不要把全部代码贴出来)等可以反映项目结果的内容。提交作业的时候必须有这个项目说明文档,否则会影响最终评分。

答:

1. 创建Spring的配置文件application-context.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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd" >
     
    <context:component-scan base-package="com.netease.course" />
     
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
         
        <tx:annotation-driven transaction-manager="txManager"/>
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
         
    <context:property-placeholder location="db.properties" />
</beans>

2. db.properties略

3. 创建Account.java

public class Account {
    private int userId;
    private double balance;
    // 所需getters和setters
}

4a. 使用Spring JDBC实现AccountDao

@Repository
public class AccountDao {
    private JdbcTemplate jdbcTemplate;
     
    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
     
    public void resetMoney() {
        jdbcTemplate.update("update account set balance=1000");
    }
     
    public List<Account> accountList() {
        return this.jdbcTemplate.query("select * from account", new RowMapper<Account>() {
            public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                Account account = new Account();
                account.setUserId(rs.getInt("id"));
                account.setBalance(rs.getDouble("balance"));
                return account;
            }
        });
    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void transferMoney(Long srcUserId, Long targetUserId, double count) {
        // srcUserId及targetUserId为转帐用户标识
        updateBalance(-count, srcUserId);
    updateBalance(count, targetUserId);
    }   
     
    private void updateBalance(double count, Long userId) {
        this.jdbcTemplate.update("update account set balance=balance+? where id=?", count, userId);
    }
     
    private void throwException() {
        throw new RuntimeException("UPDATE ERROR");
    }
}

4b. 使用MyBatis实现AccountDao 

<?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=".matthew.dao.AccountDao">
    <resultMap type="Account" id="AccountResult">
        <result property="userId" column="userId" />
        <result property="balance" column="balance" />
    </resultMap>
    <update id="reset">
        update account set balance=1000
    </update>
 
    <select id="getUserList" resultMap="AccountResult">
        select * from account
    </select>
    <update id="updateBalance">
        update account set balance=balance+#{param2} where userId=#{param1}
    </update>
</mapper>

5. main()

public static void main (String[] args) throws Exception {
    ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
    AccountDao dao = context.getBean("accountDao", AccountDao.class);
 
    dao.resetMoney();
    List<Account> accountList = dao.accountList();
    for(Account account: accountList) {
        System.out.println(account.getUserId() + ":" + account.getBalance());
    }
    try {
        dao.transferMoney(1L, 2L, 520);
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
    accountList = dao.accountList();
    for(Account account: accountList) {
        System.out.println(account.getUserId() + ":" + account.getBalance());
    }
    ((ConfigurableApplicationContext) context).close();
}

6. 运行结果

    

7. 模拟exception throw测试

    在transferMoney()中的两个update之间调用throwException();

8. 运行结果

    

原文地址:https://www.cnblogs.com/FudgeBear/p/7542990.html