@Transactional注解可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有public方法将都具有该类型的事务属性,同时,也可以在方法级别使用该注解来覆盖类级别的定义。虽然@Transactional注解可以作用于接口、接口方法、类以及类方法上,但是Spring小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
如何在事务处理中捕获异常

声明式事务处理的流程是:1)Spring根据配置完成事务定义,设置事务属性。2)执行开发者的代码逻辑。3)如果开发者的代码产生异常(如主键重复)并且满足事务回滚的配置条件,则事务回滚;否则,事务提交。4)事务资源释放。
    现在的问题是,如果开发者在代码逻辑中加入了try...catch...语句,Spring还能不能在声明式事务处理中正常得到事务回滚的异常信息?答案是不能。
1)修改@Transactional注解
我们需要将TestServiceImpl类中的@Transactional注解修改为:
@Transactional(rollbackFor= {Exception.class})
//rollbackFor指定回滚生效的异常类,多个异常类逗号分隔;
//noRollbackFor指定回滚失效的异常类
2)在catch语句中添加“throw new RuntimeException();”语句。
注意:在实际工程应用中,经常在catch语句中添加“TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();”语句即可。也就是说,不需要在@Transaction注解中添加rollbackFor属性。
package entity;

public class MyUser {
    private Integer uid;
    private String uname;
    private String usex;

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUsex() {
        return usex;
    }

    public void setUsex(String usex) {
        this.usex = usex;
    }

    public String toString() {
        return "myUser [uid=" + uid + ", uname=" + uname + ", usex=" + usex + "]";
    }
}
package service;

public interface TestService {
    public void testJDBC();
}
package service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import dao.TestDao;
import entity.MyUser;

@Service
@Transactional   //@Transactional(rollbackFor= {Exception.class})
public class TestServiceImpl implements TestService {
    @Autowired
    public TestDao testDao;

    @Override
    public void testJDBC() {
        String insertSql = "insert into user values(null,?,?)";
        // 数组param的值与insertSql语句中?一一对应
        Object param1[] = { "chenheng1", "男" };
        Object param2[] = { "chenheng2", "女" };
        Object param3[] = { "chenheng3", "男" };
        Object param4[] = { "chenheng4", "女" };
        String insertSql1 = "insert into user values(?,?,?)";
        Object param5[] = { 1, "chenheng5", "女" };
        Object param6[] = { 1, "chenheng6", "女" };
        try {
            // 添加用户
            testDao.update(insertSql, param1);
            testDao.update(insertSql, param2);
            testDao.update(insertSql, param3);
            testDao.update(insertSql, param4);
            // 添加两个ID相同的用户,出现唯一性约束异常,使事物回滚。
            testDao.update(insertSql1, param5);
            testDao.update(insertSql1, param6);
            // 查询用户
            String selectSql = "select * from user";
            List<MyUser> list = testDao.query(selectSql, null);
            for (MyUser mu : list) {
                System.out.println(mu);
            }
        } catch(Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            System.out.println("主键重复,事务回滚。");
            throw new RuntimeException();
        }
    }
}
package dao;

import java.util.List;
import entity.MyUser;

public interface TestDao {
    public int update(String sql, Object[] param);

    public List<MyUser> query(String sql, Object[] param);
}
package dao;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import entity.MyUser;

@Repository
public class TestDaoImpl implements TestDao {
    @Autowired
    // 使用配置类中的JDBC模板
    private JdbcTemplate jdbcTemplate;

    /**
     * 更新方法,包括添加、修改、删除 param为sql中的参数,如通配符?
     */
    @Override
    public int update(String sql, Object[] param) {
        return jdbcTemplate.update(sql, param);
    }

    /**
     * 查询方法 param为sql中的参数,如通配符?
     */
    @Override
    public List<MyUser> query(String sql, Object[] param) {
        RowMapper<MyUser> rowMapper = new BeanPropertyRowMapper<MyUser>(MyUser.class);
        return jdbcTemplate.query(sql, rowMapper);
    }
}
package config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration // 通过该注解来表明该类是一个Spring的配置,相当于一个xml文件
@ComponentScan(basePackages = { "dao", "service" }) // 配置扫描包
@PropertySource(value = { "classpath:jdbc.properties" }, ignoreResourceNotFound = true)
//配置多个配置文件  value={"classpath:jdbc.properties","xx","xxx"}
@EnableTransactionManagement // 开启声明式事务的支持
public class SpringJDBCConfig {
    @Value("${jdbc.url}") // 注入属性文件jdbc.properties中的jdbc.url
    private String jdbcUrl;
    @Value("${jdbc.driverClassName}")
    private String jdbcDriverClassName;
    @Value("${jdbc.username}")
    private String jdbcUsername;
    @Value("${jdbc.password}")
    private String jdbcPassword;

    /**
     * 配置数据源
     */
    @Bean
    public DriverManagerDataSource dataSource() {
        DriverManagerDataSource myDataSource = new DriverManagerDataSource();
        // 数据库驱动
        myDataSource.setDriverClassName(jdbcDriverClassName);
        ;
        // 相应驱动的jdbcUrl
        myDataSource.setUrl(jdbcUrl);
        // 数据库的用户名
        myDataSource.setUsername(jdbcUsername);
        // 数据库的密码
        myDataSource.setPassword(jdbcPassword);
        return myDataSource;
    }

    /**
     * 配置JdbcTemplate
     */
    @Bean(value = "jdbcTemplate")
    public JdbcTemplate getJdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    /**
     * 为数据源添加事务管理器
     */
    @Bean
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager dt = new DataSourceTransactionManager();
        dt.setDataSource(dataSource());
        return dt;
    }
}
package config;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import service.TestService;

public class TestJDBC {
    public static void main(String[] args) {
        // 初始化Spring容器ApplicationContext
        AnnotationConfigApplicationContext appCon = new AnnotationConfigApplicationContext(SpringJDBCConfig.class);
        TestService ts = appCon.getBean(TestService.class);
        ts.testJDBC();
        appCon.close();
    }
}