Spring(四)-- JdbcTemplate、声明式事务

1.Spring提供的一个操作数据库的技术JdbcTemplate,是对Jdbc的封装。语法风格非常接近DBUtils。

JdbcTemplate可以直接操作数据库,加快效率,而且学这个JdbcTemplate也是为声明式事务做准备,毕竟要对数据库中的数据进行操纵!

JdbcTemplate中并没有提供一级缓存,以及类与类之间的关联关系!就像是spring提供的一个DBUtils。

Spring对数据库的操作使用JdbcTemplate来封装JDBC,结合Spring的注入特性可以很方便的实现对数据库的访问操作。使用JdbcTemplate可以像JDBC一样来编写数据库的操作代码

2.为啥要使用Jdbc_template进行开发呢?

Spring对数据库的操作在jdbc上面做了深层次的封装,使用spring的注入功能,可以把DataSource注册到JdbcTemplate之中。

Spring提供的JdbcTemplate对jdbc做了封装,大大简化了数据库的操作。找到Spring JdbcTemplate源码,可以看到如下方法:

Connection con = DataSourceUtils.getConnection(getDataSource());

如果直接使用JDBC的话,需要我们加载数据库驱动、创建连接、释放连接、异常处理等一系列的动作;繁琐且代码看起来不直观。

此外,Spring提供的JdbcTempate能直接数据对象映射成实体类,不再需要获取ResultSet去获取值/赋值等操作,提高开发效率;

3.配置环境

①导入jar包

  [1]IOC容器需要的jar包

commons-logging-1.1.3.jar

spring-aop-4.0.0.RELEASE.jar  //注解会使用到的包

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE.jar

  [2]MySQL驱动、C3P0jar包

c3p0-0.9.1.2.jar

mysql-connector-java-5.1.37-bin.jar

  [3]JdbcTemplate需要的jar包

spring-jdbc-4.0.0.RELEASE.jar

spring-orm-4.0.0.RELEASE.jar

spring-tx-4.0.0.RELEASE.jar

②在IOC容器中配置数据源

<!-- 加载properties文件中 信息 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 -->
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<property name="user" value="${jdbc.username}"></property>
	<property name="password" value="${jdbc.password}"></property>
	<property name="driverClass" value="${jdbc.driver}"></property>
	<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>

其中,新建 jdbc.properties 文件

内容:

jdbc.username=root
jdbc.password=123456
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jdbc_template

③在IOC容器中配置JdbcTemplate对象的bean,并将数据源对象装配到JdbcTemplate对象中

<!-- 配置JdbcTemplate对应的bean, 并装配dataSource数据源属性-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
      <property name="dataSource" ref="comboPooledDataSource"></property>
</bean>

实验1:测试数据源

@Test
public void test() throws SQLException{
       ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
       DataSource bean = ioc.getBean(DataSource.class);
       System.out.println(bean.getConnection());
}

实验2:将emp_id=5的记录的salary字段更新为1300.00【更新操作】

不用我们自己再去获取数据库连接信息了,而是直接传递sql语句及其参数!

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
      private JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);

      @Test
      public void test() throws SQLException{
            String sql = "UPDATE employee SET salary = ? WHERE emp_id = ?";
            bean.update(sql, 1300,5);//第一个是sql语句,后面的按着顺序传入参数即可,这个update方法是接收的可变参数!
      }
}

实验3:批量插入  bean.batchUpdate(sql, list);

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
      private JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);

      @Test
      public void test() throws SQLException{
            String sql="INSERT INTO employee(emp_name,salary) VALUES(?,?)";

            List<Object[]> list = new ArrayList<Object[]>();
            list.add(new Object[]{"Tom2015",1000});
            list.add(new Object[]{"Tom2016",2000});
            list.add(new Object[]{"Tom2017",3000});

            bean.batchUpdate(sql, list);
      }
}

实验4:查询emp_id=5的数据库记录,封装为一个Java对象返回

分析:封装为一个对象返回的话,首先我们需要有一个与数据表对应的实体类!

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
      private JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);

      @Test
      public void test(){
            //需要注意的是:sql语句中的别名要与对应实体类的属性名保持一致!
            String sql = "SELECT emp_id AS empId,emp_name AS empName,salary FROM employee WHERE emp_id=?";

            //RowMapper是一个接口,这里我们使用其子类
            RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
            //最后一个参数是可变参数,用于向sql语句中依次传递参数!
            Employee employee = bean.queryForObject(sql, rowMapper, 5);
            System.out.println(employee);
      }
}

实验5:查询salary>4000的数据库记录,封装为List集合返回

可以看出,查询结果是一个实体还是一个list列表是靠bean对象的不同方法实现的!

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
      private JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);

      @Test
      public void test(){
         //需要注意的是:sql语句中的别名要与对应实体类的属性名保持一致!
           String sql = "SELECT emp_id AS empId,emp_name AS empName,salary FROM employee WHERE salary > ?";

           //RowMapper是一个接口,这里我们使用其子类
           RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<Employee>(Employee.class);
           //该query方法查询出来的是一个list列表,query方法的最后一个参数是可变参数!
           List<Employee> list = bean.query(sql, rowMapper, 4000);

           for (Employee employee : list) {
                System.out.println(employee);
           }
     }
}

实验6:查询最大salary

public class TestJDBCTemplate {

    private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
    private JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);

    @Test
    public void test01(){
         String sql = "SELECT MAX(salary) FROM employee";
         //需要指定返回值的类型,而且类型必须是包装类型
         Double maxSalary = bean.queryForObject(sql, Double.class);
         System.out.println(maxSalary);
    }
}

实验7:使用带有具名参数的SQL语句插入一条员工记录,并以Map形式传入参数值

具名参数:是指基于名称的,前面我们使用的都是用 ? 作为占位符,是基于位置的!

如果要使用具名参数的sql语句就必须在spring配置文件中配置NamedParameterJdbcTemplat这个模板类

原来的JdbcTemplate,因为JdbcTemplate不能完成这样的任务!

在 applicationContext.xml 文件中继续配置

<!-- 为了执行带有具名参数的SQL语句,需要配置NamedParameterJdbcTemplate -->
<!-- 该NamedParameterJdbcTemplate类没有无参构造器,需要传入JdbcTemplate对象或者数据源对象[DataSource] -->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <!-- 不能使用property标签配置 -->
    <constructor-arg ref="jdbcTemplate"></constructor-arg>
</bean>

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

      @Test
    public void test01(){
         NamedParameterJdbcTemplate bean2 = ioc.getBean(NamedParameterJdbcTemplate.class);
         String sql="INSERT INTO employee(emp_name,salary) VALUES(:paramName,:paramSalary)";//具名参数与map 的key 值相同
         Map<String,Object> paramMap = new HashMap<String,Object>();
         paramMap.put("paramName","张学友" );
         paramMap.put("paramSalary",1000);

         bean2.update(sql, paramMap);
    }
}

实验8:重复实验7,以SqlParameterSource形式传入参数值

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

      @Test
    public void test01(){
         NamedParameterJdbcTemplate bean2 = ioc.getBean(NamedParameterJdbcTemplate.class);
         String sql="INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";
         //该BeanPropertySqlParameterSource类构造器需要一个对象参数,该对象参数是一个封装了sql语句参数的对象!
         //此时要求对象的属性名要和sql中的参数名保持一致!这里我们使用Employee对象来完成
         Employee employee= new Employee("郭富城", 1500);
         //以实体对象的形式封装具名参数和值
         SqlParameterSource source = new BeanPropertySqlParameterSource(employee);

         bean2.update(sql, source);
    }
}

实验9:创建JdbcTemplateDao,自动装配JdbcTemplate对象

1.创建dao类:

@Repository
public class JdbcTemplateDao {
     @Autowired
     private JdbcTemplate jdbcTemplate;

     public void update(){
         String sql = "DELETE FROM employee where emp_id = ?";
         jdbcTemplate.update(sql, 15);
     }
}

2.测试该dao

public class TestJDBCTemplate {

      private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
      private JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);

      private JdbcTemplateDao dao = ioc.getBean(JdbcTemplateDao.class);
      @Test
      public void testEmployeeDao(){
           dao.update();
      }
}

声明式事务:

1.导jar包:

- com.springsource.net.sf.cglib-2.2.0.jar

- com.springsource.org.aopalliance-1.0.0.jar

- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

- commons-logging-1.1.3.jar

- spring-aop-4.0.0.RELEASE.jar

- spring-aspects-4.0.0.RELEASE.jar

- spring-beans-4.0.0.RELEASE.jar

- spring-context-4.0.0.RELEASE.jar

- spring-core-4.0.0.RELEASE.jar

- spring-expression-4.0.0.RELEASE.jar

- spring-jdbc-4.0.0.RELEASE.jar

- spring-orm-4.0.0.RELEASE.jar

- spring-tx-4.0.0.RELEASE.jar

- c3p0-0.9.1.2.jar

- mysql-connector-java-5.1.37-bin.jar

2.创建一份jdbc.properties文件 :

jdbc.user=root

jdbc.passowrd=123456

jdbc.url=jdbc:mysql://localhost:3306/tx

jdbc.driver=com.mysql.jdbc.Driver

3.配置文件,在 JdbcTemplate的配置信息的基础上

<context:component-scan base-package="com.neuedu"></context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="user" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
      <property name="driverClass" value="${jdbc.driver}"></property>
      <property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>
<!-- 以上时JdbcTemplate配置所需 -->

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

<!-- 开启基于注解的声明式事务 -->
<!-- 有时候不需要transaction-manager 因为默认值为transactionManager -->
<!-- 如果事务管理器的id为transactionManager就不用写 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

4.在需要进行事务控制的方法上加注解:@Transactional

5.测试数据源

public class TestDataSource {

       private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

       @Test
       public void test() throws Exception {
             DataSource bean = ioc.getBean(DataSource.class);
             System.out.println(bean.getConnection());
       }
}

6.创建 Dao 类

Dao 层的四个方法

@Repository
public class BookDao {

	@Autowired
	private JdbcTemplate template;

	public double findPriceByIsbn(String isbn){
		String sql = "SELECT price FROM book WHERE isbn = ?";
		Double price = template.queryForObject(sql, Double.class, isbn);
		return price;
	}

	public void updateBookStock(String isbn){
		String sql="UPDATE book_stock SET stock = stock -1 WHERE isbn = ?";
		template.update(sql, isbn);
	}

	public void updateAccount(String username,Double price){
		String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
		template.update(sql, price,username);
	}

	//演示事务的传播机制
	public void updateBookPrice(double price,String isbn){
		String sql ="UPDATE book SET price = ? WHERE isbn = ?";
		template.update(sql, price,isbn);
	}
}

7.创建 Service 层

@Service
public class BookService {

	@Autowired
	private BookDao bookDao;

	@Transactional
	public void doCash(String isbn,String username){
		double price = bookDao.findPriceByIsbn(isbn);
		bookDao.updateBookStock(isbn);
		try {
			Thread.sleep(1000*5);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		bookDao.updateAccount(username, price);
	}

	@Transactional
	public void updateBookPrice(double price,String isbn){
		bookDao.updateBookPrice(price, isbn);
	}
}

8.测试 Service 中的 doCash 方法:

public class TestBookService {

	private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
	@Test
	public void test() {
		BookService bean = ioc.getBean(BookService.class);
		bean.doCash("ISBN-001", "Tom");
	}
}

事务的设置

  - 事务的传播行为
  - 事务的隔离级别
  - 事务根据什么异常不进行回滚
  - 事务的超时属性
  - 事务的只读属性

事务的传播行为:   -----propagation

- REQUIRED :【默认】

如果外层有事务,就会调用外层的事务

如果外层没事务,就自己启动个事务,在自己的事务中运行

当第二个发生异常,第一个也跟着回滚

@Transactional(propagation=Propagation.REQUIRED)
public void doCash(String isbn,String username){
      double price = bookDao.findPriceByIsbn(isbn);
      bookDao.updateBookStock(isbn);
      bookDao.updateAccount(username, price);
}

@Transactional(propagation=Propagation.REQUIRED)
public void updateBookPrice(double price,String isbn){
      bookDao.updateBookPrice(price, isbn);
}

- REQUIRES_NEW:

一定会运行自己的事务

如果外层有事务,那就挂起外层事务

等到自己的事务运行完毕,在继续外层事务

当第二个发生异常,也不会回滚

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void doCash(String isbn,String username){
      double price = bookDao.findPriceByIsbn(isbn);
      bookDao.updateBookStock(isbn);
      bookDao.updateAccount(username, price);
}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateBookPrice(double price,String isbn){
      bookDao.updateBookPrice(price, isbn);
}

- SUPPORTS :

如果外层有事务,那么就在外层事务中运行

如果没有,自己也不启动新事务

总结:

事务的隔离级别:  ----- isolation

比如,当前数据库中的price 为100,输出100;

然后将 price 改为 200,再输出还会是100,默认是REPEATABLE_READ,保持事务数据一致性

改为:READ_COMMITTED,就会随着数据库中的数据的更改,输出也更改

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
public void doCash(String isbn,String username){
     double price = bookDao.findPriceByIsbn(isbn);
     System.out.println(price);
     bookDao.updateBookStock(isbn);
     bookDao.updateAccount(username, price);
}

不回滚: ------- noRollbackFor={ ArithmeticExpection.class }

默认出现异常回滚,设置之后出现异常不回滚

花?括号内写异常名称

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED
                              ,noRollbackFor={ArithmeticException.class}
                              )
public void doCash(String isbn,String username){
      double price = bookDao.findPriceByIsbn(isbn);
      bookDao.updateBookStock(isbn);
      int i = 10/0;//异常
      bookDao.updateAccount(username, price);
}

事务的超时属性:  ------ tiomeout

事务超时=事务开始时到最后一个Statment创建时时间 + 最后一个Statment 的执行时超市时间

事务超时时间设置为3秒,sql执行时间为1秒时,事务的不超时的

事务超时时间设置为3秒,sql执行时间为5秒时,这样事务超时!

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED
                              ,noRollbackFor={ArithmeticException.class}
                              ,timeout=3)
public void doCash(String isbn,String username){
       double price = bookDao.findPriceByIsbn(isbn);
       bookDao.updateBookStock(isbn);
       try {
             Thread.sleep(1000*5);
           } catch (InterruptedException e) {
                  e.printStackTrace();
            }
       bookDao.updateAccount(username, price);
}

事务的只读属性: -----readOnly

查询性能更高

时间: 2024-11-03 20:29:48

Spring(四)-- JdbcTemplate、声明式事务的相关文章

spring+mybatis之声明式事务管理初识(小实例)

前几篇的文章都只是初步学习spring和mybatis框架,所写的实例也都非常简单,所进行的数据访问控制也都很简单,没有加入事务管理.这篇文章将初步接触事务管理. 1.事务管理 理解事务管理之前,先通过一个例子讲一下什么是事务管理:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都不执行.如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元:如果银行卡扣钱失败但是ATM却出

全面分析 Spring 的编程式事务管理及声明式事务管理

转自:http://www.open-open.com/lib/view/open1414310646012.html 关于本教程 本教程将深切讲授 Spring 庞杂而丁壮夜的事务治理功用,包括编程式事务和声明式事务.经由进程对本教程的进修,您将可以理解 Spring 事务治理的实质,并无邪运用之. 先决前提 本教程假定您已掌控了 Java 根蒂根抵常识,并对 Spring 有一定意见.您还需求具有根抵的事务治理的常识,好比:事务的界说,隔离级其他概念,等等.本文将直接行使这些概念而不做具体正

全面分析 Spring 的编程式事务管理及声明式事务管理--转

开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带

spring事务管理——编程式事务、声明式事务

本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带有 512MB 内存(

Spring学习之Spring的声明式事务管理详解

声明式事务管理 大多数Spring用户选择声明式事务管理的原因是,这个是对应用代码影响最小的选择,因此也最符合 非侵入式 轻量级容器的理念. Spring声明式事务管理可以在任何环境下使用.只需更改配置文件, 它就可以和JDBC.JDO.Hibernate或其他的事务机制一起工作. Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上. Spring提供了声明式的回滚规则. Spring允许你通过AOP定制事务行为.(例如,如果需要,你可以在事务回滚中插入定制的行为. 你也可以增

Spring声明式事务配置管理方法

环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add librarys: 打开Add Libraries对话框,然后选定 MyEclipse Libraries: 点击Next,找到Spring 2.0 aop Libraries并勾选上,点击finsh即可. 如果在项目里面能看到下面的库文件,说明已经安装成功. 事务配置 首先在/WEB-INF/applica

Spring声明式事务的配置~~~

/*2011年8月28日 10:03:30 by Rush  */ 环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add librarys: 打开Add Libraries对话框,然后选定 MyEclipse Libraries: 点击Next,找到Spring 2.0 aop Libraries并勾选上,点击finsh即可. 如果在项目里面能看到下面的库文件,

分析 Spring 的编程式事务管理及声明式事务管理(转)

开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带

Spring声明式事务配置管理方法(转)

项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add librarys: 打开Add Libraries对话框,然后选定 MyEclipse Libraries: 点击Next,找到Spring 2.0 aop Libraries并勾选上,点击finsh即可. 如果在项目里面能看到下面的库文件,说明已经安装成功. 事务配置 首先在/WEB-INF/applicationC

Spring 声明式事务配置管理方法

环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可.添加方法: 点击项目右键->Build Path->Add librarys: 9(V[673_ZRWHP65{U81_AW0 打开Add Libraries对话框,然后选定 MyEclipse Libraries: image 点击Next,找到Spring 2.0 aop Libraries并勾选上,点击finsh即可. image 如果在项目里面能看到下面的库文