美文网首页
Spring学习day-65:事务和注解

Spring学习day-65:事务和注解

作者: 开源oo柒 | 来源:发表于2019-10-22 22:42 被阅读0次

    一、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>
    

    相关文章

      网友评论

          本文标题:Spring学习day-65:事务和注解

          本文链接:https://www.haomeiwen.com/subject/usecvctx.html