Spring ( 五 )Spring之数据访问与事务管理

个人博客网:https://wushaopei.github.io/    (你想要这里多有)

一、Spring之数据访问

1、Spring数据访问工程环境搭建


jdbc.properties配置文件:

jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jdbctemplate

applicationContext.xml配置文件:

        <!-- 加载jdbc.properties配置文件 -->	
	<context:property-placeholder location="classpath:jdbc.properties"/>
	<!-- 数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
		<property name="user" value="${jdbc.user}" />
		<property name="password" value="${jdbc.password}" />
		<property name="driverClass" value="${jdbc.driverClass}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
	</bean>
	
	<!-- jdbcTempalte是一个工具类,专门用来执行sql语句 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"/>
	</bean>

测试代码:

     @Test
     public void testDataSource() throws Exception {
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
	System.out.println( dataSource.getConnection() );
	System.out.println(applicationContext.getBean("jdbcTemplate"));
    }

2、Spring之JdbcTemplate使用

在Spring中提供了对jdbc的封装类叫JdbcTemplate。它可以很方便的帮我们执行sql语句,操作数据库。

先准备单表的数据库数据

drop database  if exists jdbctemplate;

create database jdbctemplate;

use jdbctemplate;

CREATE TABLE `employee` (
  `id` int(11) primary key AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `salary` decimal(11,2) DEFAULT NULL
);

insert  into `employee`(`id`,`name`,`salary`) 
values (1,'李三',5000.23),(2,'李四',4234.77),(3,'王五',9034.51),
(4,'赵六',8054.33),(5,'孔七',6039.11),(6,'曹八',7714.11);

select * from employee;

创建一个与数据库表对应的javaBean类

3、将id=5的记录的salary字段更新为1300.00

@ContextConfiguration(locations="classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTempalteTest {
	@Autowired
	JdbcTemplate jdbcTemplate;
	@Test
	public void test2() throws Exception {
		// 实验2:将id=5的记录的salary字段更新为1300.00
		String sql = "update employee set salary = ? where id = ?";
		System.out.println( jdbcTemplate.update(sql, new BigDecimal(1300),5) );
	}
}

4、批量插入

        @Test
	public void test3() throws Exception {
		String sql = "insert into employee(`name`,`salary`) values(?,?)";
//		jdbcTemplate.update(sql, "尚硅谷94V587",new BigDecimal(100000)); //插入一条
		
		List<Object[]> batchArgs = new ArrayList<Object[]>();
		batchArgs.add(new Object[] {"新来的1",new BigDecimal(30000)});
		batchArgs.add(new Object[] {"新来的2",new BigDecimal(40000)});
		batchArgs.add(new Object[] {"新来的3",new BigDecimal(50000)});
		
		jdbcTemplate.batchUpdate(sql, batchArgs);
	}

分析图解:

数据库结果:

5、查询id=5的数据库记录,封装为一个Java对象返回

        @Test
	public void test4() throws Exception {
		String sql = "select id,name,salary from employee where id = ?";
//		rowMapper是将查询到的ResultSet的每一行记录转换成为一个javaBean对象
			
		Employee employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Employee>(Employee.class), 5);
		System.out.println( employee );
	}

6、查询salary>4000的数据库记录,封装为List集合返回

public void test5() throws Exception {
//	QueryRunner
//		update()	====>>>>		insert、update、delete
//		到底是查一条,还是查多条记录,由ResultSetHandler决定
//	Jdbctempalte
//		update()	====>>>>		insert、update、delete
//	queryForObject		查一个对象
//	query				查多个对象
		
	String sql = "select id,name,salary from employee where salary > ?";
	List<Employee> employees = jdbcTemplate.query(sql,
			new BeanPropertyRowMapper<Employee>(Employee.class), new BigDecimal(4000));
	employees.forEach(System.out::println);
	}

7、查询最大salary

	@Test
	public void test6() throws Exception {
		String sql = "select max(salary) from employee";
		BigDecimal maxSalary = jdbcTemplate.queryForObject(sql, BigDecimal.class);
		System.out.println( maxSalary );
	}

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

配置NamedParameterJdbcTemplate

        <!-- 配置可以解析执行具名参数的sql的JdbcTemplate -->
	<bean id="namedParameterJdbcTemplate" 
		class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
		<constructor-arg index="0" ref="dataSource" />
	</bean>

Test中添加以下注解,用于注入xml中解析执行具名参数所用

        @Test
	public void test7() throws Exception {
		/**
		 * :name	就是占位符,参数。名是name。	一起,就是具名参数
		 */
		String sql = "insert into employee(`name`,`salary`) values(:name,:salary)";
		
		Map<String, Object>paramMap = new HashMap<>();
		paramMap.put("name", "这是具名参数的name");
		paramMap.put("salary", new BigDecimal(100000));
		
		namedParameterJdbcTemplate.update(sql, paramMap);
	}

Mysql 结果:

9、重复8,以SqlParameterSource形式传入参数值

        @Test
	public void test8() throws Exception {
		/**
		 * :name	就是占位符,参数。名是name。		一起,就是具名参数
		 */
		String sql = "insert into employee(`name`,`salary`) values(:name,:salary)";
		Employee employee = new Employee(null, "新插入的帅哥", new BigDecimal(3000));
		namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee));
	}

10、创建Dao,自动装配JdbcTemplate对象

@Repository
public class EmployeeDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public Employee queryEmployeeById(Integer id) {
		String sql = "select id,name,salary from employee where id = ?";
		Employee employee = jdbcTemplate.queryForObject(sql,
				new BeanPropertyRowMapper<Employee>(Employee.class), id);
		return employee;
	}
	
}

配置内容:

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

测试代码:

@ContextConfiguration(locations = "classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class JdbcTempalteTest {

	@Autowired
	JdbcTemplate jdbcTemplate;
	
	@Autowired
	NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	
	@Autowired
	EmployeeDao employeeDao;

	@Test
	public void test9() throws Exception {
		System.out.println( employeeDao.queryEmployeeById(1) );
	}
}

11、通过继承JdbcDaoSupport创建JdbcTemplate的Dao

@Repository
public class EmployeeDao extends JdbcDaoSupport {

//	@Autowired
//	private JdbcTemplate jdbcTemplate;
	
	public Employee queryEmployeeById(Integer id) {
		String sql = "select id,name,salary from employee where id = ?";
		Employee employee = getJdbcTemplate().queryForObject(sql,
				new BeanPropertyRowMapper<Employee>(Employee.class), id);
		return employee;
	}
	
	@Autowired
	public void setJdbcTemplate2(JdbcTemplate jdbcTemplate) {
		setJdbcTemplate(jdbcTemplate);
	}
	
}

源码分析方法实现与调用过程:

二、声明式事务

事务分为声明式和编程式两种:

声明式事务:声明式事务是指通过注解(和xml配置)的形式对事务的各种特性进行控制和管理。

编码式(编程式)事务:指的是通过编码的方式实现事务的声明。

1、编码方式实现事务:

2、声明式事务环境搭建

2.1、准备测试数据库

##创建tx数据库
drop database if exists `tx`;
CREATE database `tx`;
##切换tx数据库
USE `tx`;

##删除用户表
DROP TABLE IF EXISTS `user`;
##创建用户表
CREATE TABLE `user` (
  `id` int primary key auto_increment,	
  `username` varchar(50) NOT NULL,
  `money` int(11) DEFAULT NULL
);
##插入数据
insert  into `user`(`username`,`money`) values ('张三',1000),('李四',1000);

##删除图书表
drop table if exists `book`;
##创建图书表
create table `book`(
    `id` int primary key auto_increment,
    `name` varchar(500) not null,
    `stock` int
);
##插入数据
insert into book(`name`,`stock`) values('java编程思想',100),('C++编程思想',100);

##查看数据
select * from book;
select * from user;

   2.2、创建一个Java工程,导入Jar包

@Repository
public class UserDao {

	@Autowired
	JdbcTemplate jdbcTemplate;

	public void updateUser() {
		jdbcTemplate.update("update user set username = '用户表被修改了'");
	}
}

@Repository
public class BookDao {

	@Autowired
	JdbcTemplate jdbcTemplate;

	public void updateBook() {
		jdbcTemplate.update("update book set name = '图书表被修改了'");
	}
}

Service

@Service
public class TransactionService {

	@Autowired
	private UserDao userDao;

	@Autowired
	private BookDao bookDao;

	public void multiUpdate() {
		userDao.updateUser();
		int i = 12 / 0 ;
		bookDao.updateBook();
	}

}

3、测试Service的默认事务

【1】测试service服务层的默认事务

默认一个sql一个事务

@ContextConfiguration(locations="classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {
	
	@Autowired
	TransactionService transactionService;

	@Test
	public void testMultiUpdate() throws Exception {
		transactionService.multiUpdate();
	}
	
}

异常的演示

Spring事务引入的分析------PlatformTransactionManager类简单介绍

4、使用Spring的注解声明事务管制

【1】测试Spring的声明式事务

TransactionService中的修改

        /**
	 * @Transactional表示当前方法有事务管理
	 */
	@Transactional
	public void multiUpdate() {
		userDao.updateUser();
//		int i = 12 / 0 ;
		bookDao.updateBook();
	}

配置文件中的内容:

        <!-- 
    		配置事务管理===等价于切面
	 -->
	<bean id="transactionManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- dataSource一定要是操作数据库的数据源 -->
		<property name="dataSource" ref="dataSource" />	
	</bean>
	
	<!-- 
		开启事务的注解支持==做aop
			transaction-manager="transactionManager" 使用哪个事务管理器来管理事务
				如果事务管理器的id就叫transactionManager,
				则:属性transaction-manager可以省略
	 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>

5、noRollbackFor和noRollbackForClassName测试不回滚的异常

【1】noRollbackFor和noRollbackForClassName测试不回滚的异常

        /**
	 * @throws FileNotFoundException 
	 * @Transactional表示当前方法有事务管理<br/>
	 * 	Spring底层默认是回滚运行时异常,以运行时子异常<br/>
	 *  noRollbackFor设置哪些异常不回滚事务<br/>
	 *  noRollbackForClassName设置哪些类型的异常不回滚事务<br/>
	 */
	@Transactional(noRollbackForClassName="java.lang.ArithmeticException")
	public void multiUpdate() throws FileNotFoundException {
		userDao.updateUser();
		int i = 12 / 0 ;
		bookDao.updateBook();
	}

运行时异常回滚

编译异常:不回滚

6、自定义设置回滚异常

【1】rollbackFor和rollbackForClassName回滚的异常

        /**
	 * @throws FileNotFoundException 
	 * @Transactional表示当前方法有事务管理<br/>
	 * 	Spring底层默认是回滚运行时异常,以运行时子异常<br/>
	 * 	rollbackFor是设置哪个异常回滚事务<br/>
	 *  rollbackForClassName是设置哪个异常类名也会回滚事务<br/>
	 */
	@Transactional()
	public void multiUpdate() throws FileNotFoundException {
		userDao.updateUser();
//		int i = 12 / 0 ;
		int i = 12 ;
		if (i == 12) {
			throw new FileNotFoundException("asdf");
		}
		bookDao.updateBook();
	}

7、事务的只读属性

实验4:测试readOnly只读属性

        /**
	 * @throws FileNotFoundException 
	 * @Transactional表示当前方法有事务管理<br/>
	 * 	Spring底层默认是回滚运行时异常,以运行时子异常<br/>
	 * readOnly 设置当前执行的sql语句是不是只是select查询
	 * 	如果设置为false就允许执行insert,delete、update
	 */
	@Transactional(readOnly=true)
	public void multiUpdate() throws FileNotFoundException {
		userDao.updateUser();
		bookDao.updateBook();
	}

8、事务超时属性timeout(秒为单位)

        /**
	 * @throws FileNotFoundException 
	 * @throws InterruptedException 
	 * @Transactional表示当前方法有事务管理<br/>
	 * 	Spring底层默认是回滚运行时异常,以运行时子异常<br/>
	 * timeout设置连接的超时属性。
	 * timeout=3表示3秒后不允许再执行sql语句
	 */
	@Transactional(timeout=3)
	public void multiUpdate() throws InterruptedException {
		userDao.updateUser();
		Thread.sleep(4000);
		bookDao.updateBook();
	}

10、事务的传播特性propagation

什么是事务的传播行为:

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。

事务的传播特性,有以下几种类型:

11、注解演示事物传播特性

UserService

BookService

TransactionService

实验1:大小事务传播特性都是REQUIRED

        @Transactional(propagation = Propagation.REQUIRED)
	public void multiTransaction() {
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateBook() {
        @Transactional(propagation=Propagation.REQUIRED)
        public void updateUser() {

实验2:大小事务传播特性都是REQUIRES_NEW

        @Transactional(propagation = Propagation.REQUIRES_NEW)
	public void multiTransaction()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser()

实验3:大事务是REQUIRED,小事务都是REQUIRES_NEW

        @Transactional(propagation = Propagation.REQUIRED)
	public void multiTransaction()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser()

实验3跟实验2一样。

实验4:大事务是REQUIRED,小1REQUIRED,小2REQUIRES_NEW

	@Transactional(propagation = Propagation.REQUIRED)
	public void multiTransaction()
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateBook()
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser()

三、xml配置式事务声明

去掉。所有@Transactional的注解。

配置文件内容:

<?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:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

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

	<!-- 加载jdbc.properties配置文件 -->	
	<context:property-placeholder location="classpath:jdbc.properties"/>
	<!-- 数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
		<property name="user" value="${jdbc.user}" />
		<property name="password" value="${jdbc.password}" />
		<property name="driverClass" value="${jdbc.driverClass}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
	</bean>
	
	<!-- jdbcTempalte是一个工具类,专门用来执行sql语句 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />	
	</bean>
	
	<!-- 配置事务特性 -->
	<tx:advice id="tx_advice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 	
				配置一个或多个方法的特性
				<tx:method name="save*" propagation="REQUIRED"/>
					name表示方法名
						save*表示方法名以save打头的方法都算
					propagation="REQUIRED"表示必须要有事务
			 -->
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<!-- 
				精确匹配方法名
					精确匹配优先 ===>>> 半模糊(update*) ====>>>> *
			 -->
			<tx:method name="multiTransaction" propagation="REQUIRED"/>
			<tx:method name="updateUser" propagation="REQUIRES_NEW"/>
			<!-- 
				*表示剩下的方法
				read-only="true"会做一些优化
			 -->
			<tx:method name="*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 
		配置代理
	 -->
	<aop:config>
		<!-- advisor是配置切面 -->
		<aop:advisor advice-ref="tx_advice" 
			pointcut="execution(public * com.webcode.service..*Service*.*(..))"/>
	</aop:config>

</beans>

四、Spring整合Web

1、在web工程中添加Spring的jar包

Spring的核心包
    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
aop包
    spring-aop-4.0.0.RELEASE.jar
    spring-aspects-4.0.0.RELEASE.jar
    com.springsource.org.aopalliance-1.0.0.jar
    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
JDBC-ORM包
    spring-jdbc-4.0.0.RELEASE.jar
    spring-orm-4.0.0.RELEASE.jar
    spring-tx-4.0.0.RELEASE.jar
Spring的web整合包
    spring-web-4.0.0.RELEASE.jar
测试包
    spring-test-4.0.0.RELEASE.jar
  • ServletContext在web工程启动的时候创建
  • 在Web工程停止的时候销毁

整合Spring和Web容器分两个步骤:

1、导入spring-web-4.0.0.RELEASE.jar

2、在web.xml配置文件中配置org.springframework.web.context.ContextLoaderListener监听器监听ServletContext的初始化

3、在web.xml配置文件中配置contextConfigLocation上下文参数。配置Spring配置文件的位置,以用于初始化Spring容器

web.xml中配置

 <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

获取WebApplicationContext上下文对象的方法如下:

方法一(推荐):

WebApplicationContextUtils.getWebApplicationContext(getServletContext())

方法二(不推荐):

getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

 

 

posted @ 2019-10-30 09:29  维宇空灵  阅读(150)  评论(0编辑  收藏  举报