主流框架二:Spring(6)Spring中的JdbcTemplate和事务控制

一、Spring中的JdbcTemplate

1、JdbcTemplate的概述

持久层总图:
持久层总图
JdbcTemplate类似于DBUtils,它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。并且我们需要导入spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)。

2、JdbcTemplate对象的创建

除了默认构造函数之外,其他的需要提供一个数据源DataSourc。既然有set方法,依据我们之前学过的依赖注入,我们可以在配置文件中配置这些对象。

3、Spring中配置数据源

除了C3P0和DBCP数据源外,Spring还提供了内置的数据源

<!-- 配置数据源-->
    <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="xmgl0609"></property>
    </bean>

3、JdbcTemplate中CRUD操作

(1)在bean.xml中配置JdbcTemplate对象进IOC容器

<!-- 配置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="xmgl0609"></property>
    </bean>

(2)利用JdbcTemplate对象基本使用

public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        JdbcTemplate jt = ac.getBean("jdbcTemplate", JdbcTemplate.class);
        //3.执行操作
        jt.execute("insert into account(name,money)values('ddd',2222)");
    }

使用BeanPropertyRowMapper来封装结果集

查询所需的方法:
查询所需的方法
例如查询操作:

public static void main(String[] args) {

        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        JdbcTemplate jt = ac.getBean("jdbcTemplate", JdbcTemplate.class);
        //3.执行操作
        //查询所有
        List<Account> accounts = jt.query("select * from account where money > ?", new BeanPropertyRowMapper<Account>(Account.class), 100f);
        for (Account account :
                accounts) {
            System.out.println(account);
        }
        
	new BeanPropertyRowMapper<Account>(Account.class)来封装结果集
	
        //查询一个
        List<Account> accounts1 = jt.query("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), 100f);
        System.out.println(accounts1.isEmpty()?"没有内容":accounts1.get(0));

    }

jdbctemplate和queryrunner的区别:
jdbctemplate和queryrunner的区别

4、JdbcTemplate应用到Dao层

对于是对数据库进行操作的对象,所以是在dao层。
使用

jdbcTemplate.query ()
jdbcTemplate.update()

(1)第一种方式:在dao中定义并注入JdbcTemplate

/**
 * dao的实现类1,dao中注入
 * @author Mango
 */
public class IAccountDaoImpl2  implements IAccountDao {

    private JdbcTemplate jdbcTemplate;
	
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 
		this.jdbcTemplate = jdbcTemplate; 
	}

    @Override
    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);
    }
	<bean id="accountDao" class="com.itheima.dao.impl.IAccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>

	<!-- 配置JdbcTemplate(数据库操作模板)-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

注意:有个小问题。就是我们的dao有很多时每个dao都有一些重复性的代码。下面就是重复代码:


private JdbcTemplate jdbcTemplate; 

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 
	this.jdbcTemplate = jdbcTemplate; 
} 

能不能把它抽取出来呢?
(2)第二种方式:让dao继承JdbcDaoSupport

JdbcDaoSupport是spring框架为我们提供的一个类,该类中定义了一个JdbcTemplate对象,我们可以直接获取使用,但是要想创建该对象,需要为其提供一个数据源

/**
* JdbcDaoSupport内部细节
*/
public class JdbcDaoSupport {
	//定义对象
    private JdbcTemplate jdbcTemplate;
	
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }
    
	//当然,我们也可以通过注入JdbcTemplate对象
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
	//set方法注入数据源,判断是否注入了,注入了就创建JdbcTemplate
    public void setDataSource(DataSource dataSource) {
        if(jdbcTemplate == null) {
            jdbcTemplate = createJdbcTemplate(dataSource);
        }
    }
	//使用数据源创建JdcbTemplate
    private JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }


}
/**
 * dao的实现类,直接继承JdbcDaoSupport(Spring内部有)
 * @author Mango
 */
public class IAccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

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

    @Override
    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);
    }

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

两种方式有什么区别呢?

第一种在Dao类中定义JdbcTemplate的方式,适用于所有配置方式(xml和注解都可以)。

第二种让Dao继承JdbcDaoSupport的方式,只能用于基于XML的方式,注解用不了。

二、Spring中的事务控制

1、Spring事务控制概述

第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。

第二:spring框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。

第三:spring的事务控制都是基于AOP(事务操作抽取出来)的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现。

2、Spring中事务控制的API介绍

(1)PlatformTransactionManager

此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,我们在开发中都是使用它的实现类。

org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用 org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate版本进行持久化数据时使用

(2)TransactionDefinition
事务的传播行为

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(一般查询操作时使用,没有事务)

(3)TransactionStatus:此接口提供的是事务具体的运行状态,方法介绍如下图:
在这里插入图片描述

3、基于XML的声明式事务控制(配置方式)重点

(1)在spring的配置文件并导入约束

此处需要导入aop和tx两个名称空间 
<?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" 
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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"> 

<!-- 配置数据源-->
    <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="xmgl0609"></property>
    </bean>

    <!-- 配置账户持久层-->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
</beans>

(2)dao层和service层(略)
dao层一般不使用 AccountDaoImpl extends JdbcDaoSupport,来实现。而选择注入JdbcTemplate对象,引入DataSource。

(3)

Spring中基于xml的声明式事务控制配置步骤

1.配置事务的管理器(DataSourceTransactionManager)

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

2.配置事务的通知

<!-- 配置事务通知(AOP内容)-->
   <tx:advice id="txAdvice" 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>

此时我们需要导入事务的约束 tx名称空间和约束,同时需要AOP
使用tx:advice配置事务通知
属性:id起名称,transaction-manager 给事务通知提供试图管理器引用

配置事务的属性

propagation="" 用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
read-only="" 用于指定事务是否只读,只有查询方法,才是设置为true。默认是false,表示读写


isolation="" :用于指定事务的隔离级别。默认是DEFAULT,表示使用数据库的默认隔离级别
timeout="“用于指定事务的超时时间,默认值是-1,表示永不超时。指定数值后,以秒为单位
rollback-for=”" :用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。
no-rollback-for="" :用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。

3.配置AOP中的通用切入点表达式
4.建立事务通知切入点表达式的对应关系

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

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

4、基于注解的配置方式

(1)导入约束,配置数据源DataSource,配置JdbcTemplate导入DataSource将service层和dao层交给spring管理

Spring中基于注解 的声明式事务控制配置步骤

1.配置事务管理器

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

2.开启spring对注解事务的支持

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

3.在需要事务支持的地方(service层)使用@Transactional注解
使用注解进行事务的属性配置

/**
 * 账户的业务层实现类
 *
 * 事务控制应该都是在业务层
 */
@Service("accountService")
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public class AccountServiceImpl implements IAccountService{

    @Autowired
    private IAccountDao accountDao;

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

    }

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

            int i=1/0;

            //2.6更新转入账户
            accountDao.updateAccount(target);
    }
}

注解的属性和xml中的属性含义一致
该注解可以出现在接口上,类上和方法上。
出现接口上,表示该接口的所有实现类都有事务支持。
出现在类上,表示类中所有方法有事务支持
出现在方法上,表示方法有事务支持。

以上三个位置的优先级:方法>类> 接口

5、不使用xml的配置方式

@EnableTransactionManagement,加上表示该类是事务管理。

/**
 * 此类为Spring的配置类,相当于bean.xml
 * @author Mango
 */
@Configuration
@ComponentScan("com.itheima")
@Import({JdbcConfig.class,TransactionConfig.class})
@PropertySource("jdbcConfig.properties")
//开启事务管理(新注解)
@EnableTransactionManagement
public class SpringConfiguration {
}
发布了47 篇原创文章 · 获赞 18 · 访问量 4869

猜你喜欢

转载自blog.csdn.net/qq_43605085/article/details/100130483