zoukankan      html  css  js  c++  java
  • spring深入学习(五)-----spring dao、事务管理

    访问数据库基本是所有java web项目必备的,不论是oracle、mysql,或者是nosql,肯定需要和数据库打交道。一开始学java的时候,肯定是以jdbc为基础,如下:

    private static int insert(Student student) {
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/samp_db";
        String username = "root";
        String password = "";
        Connection conn = null;
        int i = 0;
        String sql = "insert into students (Name,Sex,Age) values(?,?,?)";
        PreparedStatement pstmt;
        try {
            Class.forName(driver); //classLoader,加载对应驱动
            conn = (Connection) DriverManager.getConnection(url, username, password);
            pstmt = (PreparedStatement) conn.prepareStatement(sql);
            pstmt.setString(1, student.getName());
            pstmt.setString(2, student.getSex());
            pstmt.setString(3, student.getAge());
            i = pstmt.executeUpdate();
            pstmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
     
        return i;
    }

    spring对dao层提供了不同的模板类,主要如下;

    主要机制如下:

    数据源

    在spring中,数据连接是通过数据源获得的。数据源一般是由web应用服务器提供,spring中,通过jndi的方式获取,也可以直接在spring容器中配置,当然也可以通过代码的方式创建一个数据源。

    1、dbcp数据源

    jar包依赖:

    commons-dbcp2-2.1.1.jar
    commons-logging-1.2.jar
    commons-pool2-2.4.2.jar

    配置如下:

    2、c3p0数据源

    jar包依赖:

    mchange-commons-java-0.2.11.jar
    c3p0-0.9.5.2.jar

    配置如下:

    3、jndi数据源

    如果应用配置在高性能的应用服务器上,那么可能希望使用应用服务器本身提供的数据源。应用服务器的数据源使用JNDI开放调用者使用,spring专门提供了引用jndi数据源的JndiObjectFactoryBean,配置如下:

    另外spring为获取java ee资源提供了一个jee命名空间,通过jee可以有效的简化java ee资源的引用。利用jee命名空间引用jndi数据源的配置如下:

    4、DriverManagerDataSource(spring提供)

     事务

    针对事务,本人准备在后续数据库章节专门进行阐述,这里只要知道事务是为了保证一系列的操作要么同时成功,要么同时失败的机制。

    jdbc事务操作:

    在jdbc3.0之后,引入了savepoint的概念,流程如下;

    spring对事务的支持

    spring事务管理的亮点在于声明式事务管理,主要包括如下几个元素:

    1、TransactionDefinition

    定义了spring兼容的事务属性,包括事务隔离、事务传播、事务超时、只读状态等等。

    2、TransactionStatus

    代表一个事务的具体运行状态

    3、PlatformTransactionManagerspring

    提供的事务管理的对象

    spring可以支持很多orm框架,例如mybatis、jpa、hibernate等,spring提供的事务管理类如下:

    spring事务管理器实现类

    1、spring jdbc、mybatis

    基于数据源的connection访问数据库,所以可以使用DataSourceTransactionManager,配置如下:

    DataSourceTransactionManager可以使用DataSource的connection对象的commit()、rollback()等方法来管理事务。

    2、jpa

    jpa通过javax.persistence.EntityTransaction来管理jpa事务。
    其中EntityTransaction需要通过javax.persistence.EntityManager#getTransaction()获得,EntityManager则是由javax.persistence.EntityManagerFactory#createEntityManager()获取。

    事务同步管理器

    在spring中,jdbc的connection,hibernate的session等访问数据库的连接或会话对象统称为资源,这些资源在同一时刻是不能多线程共享的。那么如何解决呢?spring使用事务同步管理器(org.springframework.transaction.support.TransactionSynchronizationManager)使用ThreadLocal为不同事务线程提供了独立的资源副本,同时维护事务配置的属性和运行状态信息。

     

    spring事务配置

    1、xml配置方式

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
        <!-- 事务管理器 -->
        <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 数据源 -->
            <property name="dataSource" ref="dataSource" />
        </bean>
        <!-- 通知 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <!-- 传播行为 -->
                <tx:method name="save*" propagation="REQUIRED" />
            </tx:attributes>
        </tx:advice>
        <!-- 切面 -->
        <aop:config>
            <aop:advisor advice-ref="txAdvice"
                pointcut="execution(* com.jeenotes.ssm.service.*.*(..))" />
        </aop:config>
    </beans>

    不过这种方式一看就不方便,下面说说注解方式

    2、基于注解配置(@Transactional)

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
        <!-- 事务管理器 -->
        <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 数据源 -->
            <property name="dataSource" ref="dataSource" />
        </bean>
        <!-- 开启事务控制的注解支持,配置 Annotation 驱动,扫描@Transactional注解的类定义事务。proxy-target-class为true代表使用CGLIB类代理;false则代表使用的是jdk动态代理 -->
        <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    </beans>
    public class DefaultFooService implements FooService {
     
      public Foo getFoo(String fooName) {
        // do something
      }
     
      //方法上注解属性会覆盖类注解上的相同属性
      @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
      public void updateFoo(Foo foo) {
        // do something
      }
    }

    @Transactional注解:

    a、属性

    b、作用范围

    @Transactional注解可以应用于接口定义接口方法类定义类的public方法上。
    但是不建议使用在接口中,主要是因为当proxy-target-class="true"时,接口的实现类将会工作在非事务环境下,所以spring建议在具体的类上使用@Transactional。

    c、方法中使用@Transactional

    @Transactional(readOnly = true)
    public class DefaultFooService implements FooService {
     
      public Foo getFoo(String fooName) {
        // do something
      }
     
      // these settings have precedence for this method
      //方法上注解属性会覆盖类注解上的相同属性
      @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
      public void updateFoo(Foo foo) {
        // do something
      }
    }

    spring事务易错点

     

    1、关于spring事务,经常会有一种错误的说法,就是一个事务方法不应该调用另一个事务方法,否则将产生两个事务。

    事务的传播行为

    spring中的默认事务传播行为是PROPAGATION_REQUIRED,因此当一个方法使用@Transactional修饰,调用另一个使用@Transactional修饰的方法,则这两个方法其实是工作在同一个事务当中。

    2、多线程下的事务安全

    web容器基本都是多线程的,即使开发者认为自己没有开启多线程,但其实整个web容器为了能够处理更多的请求,都是使用共享线程池的方式来处理请求。在spring应用中,大部分的bean都是单例的,因此倒不会出现线程安全问题。

    但是dao层持有的都是connection对象,这个连接对象肯定是状态化的。spring的解决方式就是通过ThreadLocal将有状态的变量(connection等)本地线程化,实现线程安全。

    结论:在相同线程中进行相互嵌套调用的事务方法工作在相同的事务中;如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。

    3、哪些方法实现不了spring aop事务

    大家都知道aop的实现原理主要有jdk动态代理或者cglib,那么对于spring aop事务的实现,还是有一定的要求的:

    • 基于jdk动态代理的aop事务基于接口,所以要求方法必须是public修饰,并且不能使用static修饰;
    • 而基于cglib字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行aop增强,但是final、static、private修饰的方法都不能被子类覆盖,所以这些方法无法实施aop增强。
  • 相关阅读:
    泛微云桥e-Bridge 目录遍历,任意文件读取
    (CVE-2020-8209)XenMobile-控制台存在任意文件读取漏洞
    selenium 使用初
    将HTML文件转换为MD文件
    Python对word文档进行操作
    使用java安装jar包出错,提示不是有效的JDK java主目录
    Windows server 2012安装VM tools异常解决办法
    ifconfig 命令,改变主机名,改DNS hosts、关闭selinux firewalld netfilter 、防火墙iptables规则
    iostat iotop 查看硬盘的读写、 free 查看内存的命令 、netstat 命令查看网络、tcpdump 命令
    使用w uptime vmstat top sar nload 等命令查看系统负载
  • 原文地址:https://www.cnblogs.com/alimayun/p/10787529.html
Copyright © 2011-2022 走看看