• 马士兵Spring-声明式事务管理-annotation


    1.事务加在DAO层还是service层?
    service中可能多涉及多种DAO的操作,比如存了一个User之后,需要保存一条日志信息;如果在DAO中分别设置事务的话,一个DAO下面方法抛出异常了,但是不会影响到另一个DAO下面的方法,这是两个事务;因此事务要加在Service层;
     
    2.需求:存一个user的同时,记录一个日志,说这个user被存了;
     
    3.代码实现:--这边是spring管理hibernate下的transaction;
    1)DAO实现:UserDAOImpl.java  --保存User 
    package com.cy.dao.impl;
    
    import javax.annotation.Resource;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.springframework.stereotype.Component;
    
    import com.cy.dao.UserDAO;
    import com.cy.model.User;
    
    @Component
    public class UserDAOImpl implements UserDAO {
        
        @Resource
        private SessionFactory sessionFactory;
        
        //保存User
        public void save(User user) {
            Session s = sessionFactory.getCurrentSession();
            s.save(user);
        }
    
    }
    View Code

    2)DAO实现:LogDAOImpl.java  --保存Log

    package com.cy.dao.impl;
    
    import javax.annotation.Resource;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.springframework.stereotype.Component;
    
    import com.cy.dao.LogDAO;
    import com.cy.model.Log;
    
    @Component("logDAO") 
    public class LogDAOImpl implements LogDAO {
        
        @Resource
        private SessionFactory sessionFactory;
        
        //保存日志信息
        public void save(Log log) {
            Session s = sessionFactory.getCurrentSession();
            s.save(log);
            throw new RuntimeException("error!");
        }
    
    }
    View Code

    3)User实体 和 Log实体:

    User.java:

    package com.cy.model;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    
    
    @Entity
    public class User {
        private int id;
        private String username;
        private String password;
        
        @Id
        @GeneratedValue
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        
    }
    View Code

    Log.java:

    package com.cy.model;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    /**
     * 
     * @author chengyu
     * 表名和我们的类名不一样,使用@Table(name="xxx");
     */
    @Entity
    @Table(name="t_log")
    public class Log {
        private int id;
        private String msg;
        
        @Id
        @GeneratedValue
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getMsg() {
            return msg;
        }
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
    View Code

    4)UserService中保存User后,插入一条日志记录:

    package com.cy.service;
    
    import javax.annotation.Resource;
    
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.cy.dao.LogDAO;
    import com.cy.dao.UserDAO;
    import com.cy.model.Log;
    import com.cy.model.User;
    
    @Component("userService")
    public class UserService {
        
        @Resource
        private UserDAO userDAO;  
        
        @Resource
        private LogDAO logDAO;
        
        public void init() {
            System.out.println("init");
        }
        
        /**
         * 插入一个用户的同时,记录一条日志
         * @Transactional  默认情况:如果catch到任何的RuntimeException,自动回滚
         */
        @Transactional
        public void add(User user) {
            userDAO.save(user);
            Log log = new Log();
            log.setMsg("a user saved!");
            logDAO.save(log);
        }
        
        public UserDAO getUserDAO() {
            return userDAO;
        }
        
        public void setUserDAO( UserDAO userDAO) {
            this.userDAO = userDAO;
        }
        
        public void destroy() {
            System.out.println("destroy");
        }
    }

    5)beans.xml中的配置:

    <?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:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-2.5.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
               http://www.springframework.org/schema/tx
               http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
               
        <context:annotation-config />
        <context:component-scan base-package="com.cy"/>
        
        <!-- 如果下面使用到了占位符的方式,请在locations下面去找 -->
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <value>classpath:jdbc.properties</value>
            </property>
        </bean>
    
        <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean> 
        
        <!-- 初始化SessionFactory 
            annotatedClasses :  接收对那些类做了注解
        -->
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="annotatedClasses">
                <list>
                    <value>com.cy.model.User</value>
                    <value>com.cy.model.Log</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                </props>
            </property>
        </bean>
        
        <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
              <property name="sessionFactory" ref="sessionFactory" />
        </bean>
        
        <tx:annotation-driven transaction-manager="txManager"/>
        
    </beans>

    6)测试代码:

    package com.cy.service;
    
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.cy.model.User;
    
    public class UserServiceTest {
        
        @Test
        public void testAdd() throws Exception {
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
            UserService service = (UserService)ctx.getBean("userService");
            System.out.println(service.getClass());
            service.add(new User());
            ctx.destroy();
        }
    }
    View Code

    测试结果:

    因为在LogDAOImpl中插入日志时,throw new RuntimeException("error!");

    User和Log都插入失败,User插入后回滚了;

    4. @Transactional settings的配置:

    事务的传播特性:在当前的执行环境里头,到底有没有事务,另外根据这个事务的配置情况执行到我这个当前方法的时候,当前这个方法怎么样来处理这个事务;
    1.propagation:  transaction的产生过程;事务的传播方式;
    REQUIRED:@Transactional注解的默认值;   如果有就使用原来的,如果没有就创建一个新的;@Transactional(propagation=Propagation.REQUIRED)
    MANDATORY: 强制的,必须的;当前这个方法要执行,必须得有一个Transaction;如果没有transaction会抛异常;
            比如UserService中的add方法上面@Transactional(propagation=Propagation.MANDATORY),那么测试方法中调用此add方法之前,就必须得有一个transaction;
    NESTED:在一个内嵌的transaction里面执行;
          原来有一个transaction了,相当于在这个里面内嵌了一个transaction,不在原来的transaction里面执行了,而是在内嵌的transaction里面执行, 内部的transaction回滚的时候不会影响外面的
            transaction;
    NEVER: 我这个方法要执行,必须不能有事务;要是有事务,抛异常;
    NOT_SUPPORTED: 这个方法要想执行必须不能有事务;要是有事务把这个事务挂起,执行完我这个方法,原来的事务再继续;
    REQUIRES_NEW:创建一新的transaction,如果当前有transaction,将此transaction挂起;
    SUPPORTS:  支持当前的transaction,要是有我就有,要是没有我也就没有,就不在事务里执行; 
    
    2.isolation: 隔离级别;
    3.readOnly: 如果设置为true,在transaction里面没有update。insert、delete这方面操作;比如上面的例子,add方法中要设置为@Transactional(readOnly=true)的话,就会报错:
                 Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed;
                 如果确认没有写的操作,可以设置为true,这时候spring会使用只读的Connection,readOnly的connection比读写的Connection执行效率要高;可以提高性能;
                 再者,如果这个方法里面规定只能读,那么可以设置为true,防止别人进行更改操作;
    4.timeout:  一个事务如果时间太长的话,就把它终止掉;
    5.rollbackFor: 默认情况下,RuntimeException会导致回滚;HibernateException本身就是一种运行期异常;
                    可以在rollbackFor里面写清楚,什么情况下会回滚,比如写自己定义的MyException,如果抛出MyException,就会回滚;
    6.noRollbackFor:  在某些异常下不会回滚,其他的会回滚;    
    required图解:

    在执行了method1,method1里面调用了method2,如果说在method1里面已经有transaction了,那么method2里面就不需要再创建新的transaction了;

    ---------------------

  • 相关阅读:
    【VectorDemo】
    【列表迭代器:添加元素】
    【列表迭代器】
    【迭代器】
    【LinkedList】
    【list 集合的使用】
    【CollectionDemo2】
    【CollectionDemo1】
    【集合和数组的区别?】
    【到底使用那种集合?】
  • 原文地址:https://www.cnblogs.com/tenWood/p/6892009.html
Copyright © 2020-2023  润新知