Spring学习day-65:事务和注解
一、Spring中的TX事务
1.为什么使用事务?
当时学习mybatis的时,mybatis中的事务和JDBC事务是一致的,那么Spring中式如何进行事务管理的呢?
2.事务管理方式:
(1)编程式事务管理:整个事务管理都是需要程序员自己手动编写,自己提交或者回滚 。
(2)声明式事务管理:就是整个事务的管理操作,不需要我们自己书写,现在Spring已经帮你处理好了,我们自己只要代码中声明配置即可。
3.事务管理的应用场景:
当我们执行的式两条或者两条以上的添加、修改、删除的操作时才使用事务。
- 事务的四大特性:
(1)原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
(2)一致性(consistency):数据库总数从一个一致性的状态转换到另一个一致性的状态。
(3)隔离性(isolation):一个事务所做的操作(添加、修改、删除)在最终提交以前,对其他事务是不可见的。
(4)持久性(durability):一旦事务提交,则其所做的操作(添加、修改、删除)就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
4.Spring中声明式事务:
给方法增加事务 就是给切点增加通知 。
(1)切点:需要的方法(一般在Service层);
(2)通知:事务;
(3)构成切面;
-
注意:
我们增加事务的代码块不可以自己捕获异常,如果自己进行了异常的捕获,spring是没有办法得知此时的异常,此时我们配置的声明式事务就不再起作用 。
如果我们需要书写try...catch 还要结合声明式事务,这个时候就需要自己手动抛异常 。 -
代码实现:
<!--声明事务的对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"></property>
</bean>
<tx:advice id="ad" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="login" />
</tx:attributes>
</tx:advice>
<!--通过配置切面的方式增加通知-->
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.zlw.service.impl.AdminServiceImpl.*(..))"></aop:pointcut>
<aop:advisor advice-ref="ad" pointcut-ref="pt"> </aop:advisor>
</aop:config>
5.事务管理的配置属性:
(1)name="":哪些方法需要有事务控制;
(2) readonly="boolean":是否是只读事务(如果为true,告诉数据库此事务为只读事务;如果为false(默认值),事务需要提交的事务.建议新增,删除,修改)。
(3)propagation:控制事务传播行为;当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务;
(REQUIRED (默认值): 如果当前有事务,就在事务中执行,如果当前没有事务,新建一个事务;
SUPPORTS:如果当前有事务就在事务中执行,如果当前没有事务,就在非事务状态下执行。
MANDATORY:必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错。)
(4)isolation="":事务隔离级别;在多线程或并发访问下如何保证访问到的数据具有完整性的。
( DEFAULT: 默认值,由底层数据库自动判断应该使用什么隔离界别;
SERIALIZABLE: 排队操作,对整个表添加锁.一个事务在操作数据时,另一个事务等待事务操作完成后才能操作这个表。最安全的,效率最低的。
)
(5) rollback-for="异常类型全限定路径":当出现什么异常时需要进行回滚;手动抛异常一定要给该属性值。
(6)no-rollback-for="" :当出现什么异常时不滚回事务。
二、Spring中的注解
1.Spring中常见注解:
(1)@Component 创建类对象,相当于配置<bean/>; bean的ID默认为类名首字母小写,也可以指定ID,例如@Component("stu") 。
(2)@Service 与@Component功能相同;写在ServiceImpl类上。
(3)@Repository 与@Component功能相同;写在数据访问层类上。
(4)@Controller 与@Component功能相同;写在控制器类上。
(5)@Resource(不需要写对象的get/set),java中的注解,默认按照byName注入,如果没有名称对象,按照byType注入;建议把对象名称和spring容器中对象名相同 。
(6)@Autowired(不需要写对象的get/set):spring的注解,默认按照byType注入。
(7)@Value():获取properties文件中内容。
(8)@Pointcut() 定义切点 ;@Aspect() 定义切面类 ;@Before() 前置通知 ;@After 后置通知;@AfterReturning 后置通知,必须切点正确执行;@AfterThrowing 异常通知;@Arround 环绕通知 。
- 注意:
使用注解时,一定要在配置文件中声明注解扫描 。
<context:component-scan base-package="包名路径"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 代码示例:
(1)通知
package com.zlw.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AspectJAdvice {
//前置通知方法
@Before("execution(* com.zlw.pojo.User.a())")
public void before(){
System.out.println("前置通知!");
}
//环绕通知方法
@Around("execution(* com.zlw.pojo.User.a())")
public Object around(ProceedingJoinPoint point)throws Throwable{
System.out.println("环绕通知:前...");
Object o = point.proceed();
System.out.println("环绕通知:后...");
return o;
}
//后置通知方法
@After("execution(* com.zlw.pojo.User.a())")
public void after(){
System.out.println("后置通知!");
}
//异常通知方法
@AfterThrowing("execution(* com.zlw.pojo.User.a())")
public void throwsAd(){
System.out.println("异常通知!");
}
}
(2)切点:
@Component("us")
public class User {
public void a() {
System.out.println("方法A()");
}
(3)ApplicationContext文件配置:
<!-- 扫描注解 -->
<context:component-scan base-package="com.zlw.pojo,com.zlw.advice"></context:component-scan>
<!-- 扫描的是 @Aspect -->
<aop:aspectj-autoproxy> </aop:aspectj-autoproxy>
(4)测试:
package com.zlw.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zlw.pojo.User;
public class Test01 {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = app.getBean("us",User.class);
user.a();
}
}
结果
三、银行转账系统
1.需求:
(1)实现转账账号和密码的校验;
(2)实现转账金额的校验;
(3)实现收款人的账号校验;
(4)实现Spring的事务管理;
(5)使用的技术:Spring + mybatis+jsp+servlet ;
2.实现步骤:
- 数据库:
create table account(
id int(5) PRIMARY KEY auto_increment,
cno VARCHAR(50),
pwd VARCHAR(50),
money int(5)
)
- Sql语句:
(1)查询:
select * from account where cno=? and pwd =?
select * from account where cno=? and pwd =? and money>=#{param1}
select * from account where cno =?
(2)修改账号金额:
update account set money=money-#{} where cno=#{}
update account set money =money+#{} where cno =#{}
- 实体类(生成get和set方法及构造方法):
private int id;
private String cno;
private String pwd;
private int money;
- mapper映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zlw.mapper.AccountMapper">
<select id="selOne" resultType="account">
select * from account
<where>
<if test="param1!=null and param1!=''">
cno = #{param1}
</if>
<if test="param2!=null and param2!=''">
and pwd = #{param2 }
</if>
<if test="param3!=null and param3!=''">
and money >= #{param3 }
</if>
</where>
</select>
<update id="update">
update account set money = money-#{1} where cno = #{0}
</update>
<update id="update2">
update account set money = money+#{1} where cno = #{0}
</update>
</mapper>
- ApplicationContext核心配置文件:
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.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/user"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- SqlSessionFactory -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="com.zlw.pojo"></property>
</bean>
<!-- 扫描mapper -->
<bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="factory"></property>
<property name="basePackage" value="com.zlw.mapper"></property>
</bean>
<!-- 配置注解的扫描 -->
<context:component-scan base-package="com.zlw.service.impl"></context:component-scan>
<!-- 配置声明式事务 -->
<!--
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
-->
<!-- 扫描事务注解 -->
<!--
<tx:annotation-driven ></tx:annotation-driven>
-->
</beans>
- Service层:
package com.zlw.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.zlw.mapper.AccountMapper;
import com.zlw.pojo.Account;
import com.zlw.service.AccountService;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
//查询指定账户
@Override
public Account findOne(String cno, String pwd, int money) {
return accountMapper.selOne(cno, pwd, money);
}
//修改账户金额
@Override
@Transactional
public int updateMoney(String inCno, String outCno, int money) {
int n = accountMapper.update(outCno, money);
int n2 = accountMapper.update2(inCno, money);
if (n > 0 && n2 > 0) {
return 1;
}
return 0;
}
}
- controller层:
package com.zlw.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zlw.pojo.Account;
import com.zlw.service.AccountService;
public class AccountServlet extends HttpServlet {
AccountService accs ;
@Override
public void init() throws ServletException {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
accs = app.getBean("accountService",AccountService.class);
}
//判断请求信息
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getParameter("method");
if ("checkUser".equals(method)) {
checkUser(request,response);
}else{
updateMoney(request,response);
}
}
private void updateMoney(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//获取页面信息
String cno = request.getParameter("cno");
String cno2 = request.getParameter("cno2");
String mo = request.getParameter("money");
int money = 0;
if(mo!=null&&mo!=""){
money = Integer.parseInt(mo);
}
//处理
int n = accs.updateMoney(cno2, cno, money);
if(n>0){
response.sendRedirect(request.getContextPath()+"/success.jsp");
}else{
request.setAttribute("error", "转账失败!");
request.getRequestDispatcher("/account.jsp").forward(request, response);
}
}
private void checkUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
//接收页面数据
String cno = request.getParameter("cno");
String pwd = request.getParameter("pwd");
String my = request.getParameter("money");
System.out.println(my);
int money =0 ;
if(my!=null&&my!=""){
money = Integer.parseInt(my);
}
System.out.println(cno+"---"+pwd+"--"+money);
//处理信息
Account account = accs.findOne(cno, pwd, money);
//响应
if(account!=null){
response.getWriter().println("true");
}else{
response.getWriter().println("false");
}
}
}
- 页面展示jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>银行转账系统</title>
<script type="text/javascript" src="js/jquery-1.12.3.min.js"></script>
<script type="text/javascript">
var flage1 = false;
var flage2 = false;
var flage3 = false;
$(function() {
//校验账户
$("#inp1").blur(function() {
var cno = $("#inp").val();
var pwd = $("#inp1").val();
//发送ajxa请求
$.post("accountServlet?method=checkUser", "cno=" + cno + "&pwd=" + pwd, function(re) {
if (re) {
flage1 = true;
$("#inp1_span").html("√ 汇款人信息正确!").css("color", "green");
} else {
$("#inp1_span").html("X 汇款人信息错误!").css("color", "red");
}
}, "json")
})
//判断金额
$("#inp2").blur(function() {
var cno = $("#inp").val();
var pwd = $("#inp1").val();
var money = $("#inp2").val();
$.post("accountServlet?method=checkUser", "cno=" + cno + "&pwd=" + pwd + "&money=" + money, function(data1) {
if (data1) {
flage2 = true;
$("#inp2_span").html("√ 汇款金额正确!").css("color", "green");
} else {
$("#inp2_span").html("X 汇款金额错误!").css("color", "red")
}
}, "json")
})
//判断收款人信息
$("#inp3").blur(function() {
var cno2 = $("#inp3").val();
var cno = $("#inp").val();
if (cno == cno2) {
alert("两个账号不能相同!")
} else {
$.post("accountServlet?method=checkUser", "cno=" + cno2, function(data) {
if (data) {
flage3 = true;
$("#inp3_span").html("√ 收款人信息正确!").css("color", "green");
} else {
$("#inp3_span").html("X 收款人信息错误!").css("color", "red");
}
}, "json");
}
});
})
function change() {
if (flage1 && flage2 && flage3) {
return true;
}
return false;
}
</script>
</head>
<body>
<h3>银行转账系统</h3>
<span style="color: red"> ${error }</span>
<form action="accountServlet?method=updateMoney" method="post"
onsubmit="return change()">
<p>
转账账号:<input type="text" id="inp" name="cno" />
</p>
<p>
账号密码:<input type="text" id="inp1" /><span id="inp1_span"></span>
</p>
<p>
转账金额:<input type="text" id="inp2" name="money" /><span
id="inp2_span"></span>
</p>
<p>
收款账号:<input type="text" id="inp3" name="cno2" /><span id="inp3_span"></span>
</p>
<p>
<input type="submit" value="转账" />
</p>
</form>
</body>
</html>
3.实现效果:
所有信息都正确账号或密码错误
金额错误
收款账号错误
成功页面
四、Spring总结:
1.Spring IOC(控制反转):
作用:帮助我们创捷对象的,进行代码之间的解耦;
IOC实现方式{无参构造;有参构造 ;工厂模式} ;
DI:依赖注入;给创建好的全局属性或者对象进行赋值 ;
DI注入方式:{有参构造;set方法;自动注入} ;
2.Spring AOP(面向切面编程):
代理模式{静态代理 动态代理-[JDK代理、CGLIB代理]} ;
OP作用:提升代码的扩展性 、进行了代码的解耦;
AOP的实现方式:(schema base,aspectJ);
3.Spring TX(声明式事务):
编程式事务、声明式事务;
4.Spring中的注解:
(1)创建对象 :@Component @Service @Controller ;
(2)进行值得注入: @Resource @Autowired ;
(3)AspectJ的注解: @Aspect @Befor ...
(4) 声明式事务注解 @Transactional ;
- 扫描注解:
(1)AspectJ:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(2)事务:
<tx:annotation-driven></tx:annotation-driven>