zoukankan      html  css  js  c++  java
  • 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);

     

  • 相关阅读:
    ActiveReports 报表应用教程 (9)---交互式报表之动态排序
    struts2-结果处理方式
    struts2-action的创建方式
    struts2-动态方调用
    struts2-常量配置
    struts2架构图
    maven依赖范围-Scope
    Maven在eclipse的使用入门
    Maven简介
    数组的学习与使用
  • 原文地址:https://www.cnblogs.com/wushaopei/p/11762943.html
Copyright © 2011-2022 走看看