Spring笔记----Day03----事务控制

一、JdbcTemplate:

1、使用之前,需要导入的jar包:

spring-jdbc-5.0.2.RELEASE.jar
spring-tx-5.0.2.RELEASE.jar

2、spring的事务控制的API:

<1>、PlatformTransactionManager:

(1)、作用:

该接口是提供事务操作的方法。

(2)、具体操作方法:

①、获取事务状态信息:
TransactionStatus getTransaction(TransactionDefinition definition)
②、提交事务:
void commit(TransactionStatus status)
③、回滚事务:
void rollback(TransactionStatus status)

(2)、具体管理事务(实现类)的对象:

①、使用Spring JDBC或iBatis进行持久化数据时使用:
 org.springframework.jdbc.datasource.DataSourceTransactionManager、
②、使用Hibernate版本进行持久化数据时使用:
org.springframework.orm.hibernationManager

<2>、TransactionDefinition:

(1)、作用:

该接口是提供事务定义的对象。

(2)、具体操作方法:

①、获取事务对象名称:
String getName()
②、获取事务隔离级别:
Ⅰ、方法名:
int getIsolationLevel()
Ⅱ、方法参数:
ISOLATION_DEFAULT:默认级别,归属下面某一种。
ISOLATION_READ_UNCOMMITTED:可以读未提交数据
ISOLATION_READ_COMMITTED只能读取已提交数据,解决脏读问题(Oracle默认级别)
ISOLATION_REPETABLE_READ:是否读取其他事务提交修改后的数据,解决不可重复读问题(MYSQL默认级别)
ISOLATION_SERIALIZABLE:是否读取其他事务提交添加后的数据,解决幻读问题
③、获取事务传播行为:
Ⅰ、方法名:
int getPropagationBehavior()
Ⅱ、方法参数:
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,就加入到该事务中。一般的选择,默认值。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
REQUERS_NEW:新建事务,如果当前在事务中,就把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式运行,如果当前存在事务,就抛出异常。
NESTED:如果当前存在事务,则在嵌套事务内执行;如果没有事务,则执行REQUIRED类似的操作。
④、获取事务超时时间:
Ⅰ、方法名:
int getTimeout()
Ⅱ、方法参数:
默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
⑤、获取事务是否只读:
Ⅰ、方法名:
boolean isReadOnly()
Ⅱ、方法参数:
建议查询时设置为只读。

<3>、TransactionStatus:

(1)、作用:

描述了某个时间点上事务对象的状态信息。

(2)、具体操作方法:

①、刷新事务:
void flush()
②、获取是否存在存储点:
boolean hasSavepoint()
③、获取事务是否完成:
boolean isCompleted()
④、获取事务是否为新事务:
boolean isNewTransaction()
⑤、获取事务是否回滚:
boolean isRollbackOnly()
⑥、设置事务回滚:
void setRollbackOnly()

二、配置:

1、在XML中配置JdbcTemplate的方法:

<!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

2、JdbcTemplate的操作:

<1>、获取容器:

ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

<2>、获取对象:

JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);

<3>、保存操作:

jt.update("insert into account(name,money)values(?,?)","ttt",3333f);

<4>、更新操作:

jt.update("update account set name = ?,money = ? where id = ?;","aaa",1234,1);

<5>、删除操作:

jt.update("delete from account where id = ?",11);

<6>、查询所有:

List<Account> accounts1 =
                jt.query("select * from account where money > ?", new AccountRowMapper(),4000f);
List<Account> accounts2 =
                jt.query("select * from account where money > ?", new BeanPropertyRowMapper<Account>(Account.class),4000f);
for(Account account : accounts1){
  System.out.println(account);
}

<7>、查询一个:

List<Account> accounts =
                jt.query("select * from account where money > ?", new BeanPropertyRowMapper<Account>(Account.class),4000f);
System.out.println(accounts.isEmpty()? "没有内容" : accounts.get(0));

<8>、查询返回一行一列(使用聚会函数,但不加group by子句):

Long count = jt.queryForObject("Select count(*) from account where money > ?",Long.class,1000f);
System.out.println(count);

2、pring中基于xml的声明式事务配置步骤:

<1>.配置事务管理器:

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

<2>.配置事务通知:

(1)、注意:

此时需要导入事务的约束;包括tx的名称空间和约束,aop的名称空间和约束。  

(2)、使用tx:advice标签配置事务通知时:

id:给事务通知起一个唯一id
transaction-manager:给事务通知提供一个事务管理引用

(3)、配置代码:

<tx:advice id="teAdvice" transaction-manager="transactionManager">
        <!--配置事务属性-->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

<3>、配置AOP中的通用切入点表达式:

<aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>

        <!--建立切入点表达式和事务通知的对应关系-->
        <aop:advisor advice-ref="teAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

<4>、配置事务属性:

(1)、注意:

在事务的通知tx:Advice标签内部配。

(2)、tx:method标签属性:

①、isolation="" :
用于指定事务的隔离级别,默认值是default,表示使用数据库的隔离属性
②、propagation="" :
用于指定事务的传播行为。默认值REQUIRED,表示一定会有事务。增删改的选择。查询方法可以选SUPPORTS。
③、read-only="" :
用于指定事务是否只读。只有查询方法才能设置为True,默认值是false,表示读写。
④、timeout="":
用于指定事务的超时时间。默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
⑤、rollback-for="" :
用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务都不回滚。没有默认值,表示任何异常都回滚。
⑥、no-rollback-for="" :
用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,表示任何异常都回滚。

<5>.配置数据源:

//1.准备数据源,spring的内置数据源
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/eesy");
ds.setUsername("root");
ds.setPassword("123456");

//2.创建对象
JdbcTemplate jt = new JdbcTemplate();

//3.给对象设置数据源
jt.setDataSource(ds);

//3.执行操作
jt.execute("insert into account(name,money)values('SSS',2222)");

3、pring中基于注解的声明式事务配置步骤:

<1>.配置要扫描的包:

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

<2>.配置JdbcTemplate:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<3>.配置数据源:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

<4>.配置事务管理器:

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

<5>.开启spring对注解事务的支持:

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

<6>.在需要事务支持的地方使用@Transaal注解:

//只读型事务配置,配置在类上
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
 //读写型事务配置,配置在方法上
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)

三、配置完全代码:

1、基于XML的配置:

<1>、在JdbcTemplate类中:

public class JdbcTemplateDemo1 {
    public static void main(String[] args) {
        //1.准备数据源,spring的内置数据源
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/eesy");
        ds.setUsername("root");
        ds.setPassword("123456");

        //2.创建对象
        JdbcTemplate jt = new JdbcTemplate();

        //3.给对象设置数据源
        jt.setDataSource(ds);

        //3.执行操作
        jt.execute(SQL操作语句");
    }
}

<2>、在bean.xml中:

bean.xml中:
<!--配置业务层-->
    <bean id="accountService" class="com.itheima.service.impl.AccountService">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--配置账户持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <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://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务通知-->
    <tx:advice id="teAdvice" transaction-manager="transactionManager">
        <!--配置事务属性-->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!--配置AOP-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>

        <!--建立切入点表达式和事务通知的对应关系-->
        <aop:advisor advice-ref="teAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

<3>、Service层:

public class AccountService implements IAccountService {

    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("transfer!!!");
        //根据名称查询转出账户
        Account source = accountDao.findAccountByName(sourceName);
        //根据名称查询转入账户
        Account target = accountDao.findAccountByName(targetName);
        //转出账户减钱
        source.setMoney(source.getMoney() - money);
        //转入账户加钱
        target.setMoney(target.getMoney() + money);
        //更新转出账户
        accountDao.updateAccount(source);
        //更新转入账户
        accountDao.updateAccount(target);
    }
}

<4>、Dao层:

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    public Account findAccountById(Integer accountId) {
        List<Account> accounts = super.
                getJdbcTemplate().query("select * from account where money > ?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
        return accounts.isEmpty() ? null : accounts.get(0);
    }

    public Account findAccountByName(String accountName) {
        List<Account> accounts = super.
                getJdbcTemplate().query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size() > 1){
            throw new RuntimeException("结果集不唯一!!!");
        }
        return accounts.get(0);
    }

    public void updateAccount(Account account) {
        super.getJdbcTemplate().update("update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
    }
}

<5>、定义account的封装策略:

class AccountRowMapper implements RowMapper<Account>{
    /**
     * 把结果集中的数据封装到account中,然后由spring把每个Account加到集合中
     * @param rs
     * @param i
     * @return
     * @throws SQLException
     */
    public Account mapRow(ResultSet rs, int i) throws SQLException {
        Account account = new Account();
        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
        account.setMoney(rs.getFloat("money"));
        return account;
    }

2、基于注解的配置:

<1>、在bean.xml中:

    <!--配置要扫描的包-->
    <context:component-scan base-package="com.itheima"></context:component-scan>

    <!--配置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://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

<2>、Service层:

@Service("accountService")
//只读型事务配置
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }


    //读写型事务配置
    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("transfer!!!");
        //根据名称查询转出账户
        Account source = accountDao.findAccountByName(sourceName);
        //根据名称查询转入账户
        Account target = accountDao.findAccountByName(targetName);
        //转出账户减钱
        source.setMoney(source.getMoney() - money);
        //转入账户加钱
        target.setMoney(target.getMoney() + money);
        //更新转出账户
        accountDao.updateAccount(source);
        int i =  1/0;
        //更新转入账户
        accountDao.updateAccount(target);
    }
}

<3>、Dao层:

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public Account findAccountById(Integer accountId) {
        List<Account> accounts = jdbcTemplate.query("select * from account where money > ?", new BeanPropertyRowMapper<Account>(Account.class), accountId);
        return accounts.isEmpty() ? null : accounts.get(0);
    }

    public Account findAccountByName(String accountName) {
        List<Account> accounts = jdbcTemplate.query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size() > 1){
            throw new RuntimeException("结果集不唯一!!!");
        }
        return accounts.get(0);
    }

    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
    }
}

<4>、JdbcTemplate:

public class JdbcTemplateDemo1 {
    public static void main(String[] args) {
        //1.准备数据源,spring的内置数据源
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/eesy");
        ds.setUsername("root");
        ds.setPassword("123456");

        //2.创建对象
        JdbcTemplate jt = new JdbcTemplate();

        //3.给对象设置数据源
        jt.setDataSource(ds);

        //3.执行操作
        jt.execute("insert into account(name,money)values('SSS',2222)");
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容