spring04事务

jdbcTemplate使用

xml 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>

<!--        spring里面自带了一个连接池,, 也可以用druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.12</version>
        </dependency>
    </dependencies>
java 复制代码
@Configuration
@PropertySource("classpath:db.properties")
@Data
public class JavaConfig {

    @Value("${db.username}")
    private String username;
    @Value("${db.password}")
    private String password;

    @Value("${db.url}")
    private String url;



    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setUsername(username);
        ds.setUrl(url);
        ds.setPassword(password);
        return ds;
    }


    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

}
java 复制代码
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);

        JdbcTemplate jdbcTemplate = ctx.getBean(JdbcTemplate.class);

//        // ddl 创建一张表,修改一个表的结构 data definition language  表定义操作
//        jdbcTemplate.execute();
//        // 批处理,调用这个方法
//        jdbcTemplate.batchUpdate();
//        // 增删改
//        jdbcTemplate.update();
//        jdbcTemplate.query();
//        jdbcTemplate.queryForList();
        // ddl  data  definition language
//        jdbcTemplate.execute();

        // 批处理
//        jdbcTemplate.batchUpdate()
//        jdbcTemplate.queryForList()
//        jdbcTemplate.query()

//        add(jdbcTemplate);
//        update(jdbcTemplate);
//        delete(jdbcTemplate);
//        query2(jdbcTemplate);
//        query3(jdbcTemplate);
//        query4(jdbcTemplate);
//        query5(jdbcTemplate);
        query6(jdbcTemplate);
    }


    private static  void add(JdbcTemplate jdbcTemplate){
        int row = jdbcTemplate.update("insert into user(name,address) values (?,?)", "cc", "chengdu");
        System.out.println("row = " + row);
    }


    private static void update(JdbcTemplate jdbcTemplate){
        int row = jdbcTemplate.update("update user set address = ? where id=? ", "beijing", 1);
        System.out.println("row = " + row);
    }


    private static void delete(JdbcTemplate jdbcTemplate){
        int row = jdbcTemplate.update("delete from user where  id =?", 4);
        System.out.println("row = " + row);

    }


    public static void query(JdbcTemplate jdbcTemplate){
        jdbcTemplate.query("select * from user where address = ?", new RowCallbackHandler() {
            @Override
            public void processRow(ResultSet rs) throws SQLException {
                // 外面声明一个list
            }
        },"chengdu");
    }


    public static void query2(JdbcTemplate jdbcTemplate){
        List<User> list = jdbcTemplate.query("select * from user where address = ?", new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {

                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setAddress(rs.getString("address"));
                return user;
            }
        }, "chengdu");


        System.out.println(Arrays.toString(list.toArray()));
    }


    public static void query3(JdbcTemplate jdbcTemplate){
        // 自动将查询结果  映射到对象上,,, 前提是查询的结果的列名称必须和实体的属性名称保持一致
        // 属性对不上,使用RowMapper自己去做一一映射
        List<User> list = jdbcTemplate.query("select * from user where address = ?", new BeanPropertyRowMapper<>(User.class), "chengdu");
        System.out.println(Arrays.toString(list.toArray()));
    }


    public static void query4(JdbcTemplate jdbcTemplate){
        // 查询一个用户
        User user = jdbcTemplate.queryForObject("select * from user where id = ?", new BeanPropertyRowMapper<>(User.class), 1);
        System.out.println("user = " + user);
    }


    public static void query5(JdbcTemplate jdbcTemplate){
        // 第二个参数,,设置普通对象,,,查询结果只能返回一列
        String name = jdbcTemplate.queryForObject("select name from user where id = ?", String.class, 1);
        System.out.println("name = " + name);
    }


    public static void query6(JdbcTemplate jdbcTemplate){
        // queryForList只能有一列,不能有多列
        List<String> users = jdbcTemplate.queryForList("select name from user", String.class);
        System.out.println(Arrays.toString(users.toArray()));
    }
}

传统的jdbcTemplate都是用 ?做占位符,, ,,也可以不使用?而是使用变量名称来传递参数。。使用NamedParameterJdbcTemplate...避免问号的顺序搞反了,

java 复制代码
    @Bean
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate(){
        return new NamedParameterJdbcTemplate(dataSource());
    }
java 复制代码
 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);

        NamedParameterJdbcTemplate jdbcTemplate = ctx.getBean(NamedParameterJdbcTemplate.class);
        Map<String, Object> map = new HashMap<>();
        map.put("id",2);
        map.put("address","chengdu");
        List<User> list = jdbcTemplate.query("select * from user where id>:id and address = :address", map, new BeanPropertyRowMapper<>(User.class));
        System.out.println(Arrays.toString(list.toArray()));

事务

事务的四大特性

acid

  • atomicity : 原子性,,, 一个事务中的所有操作,要么全部完成,要么全部不完成
  • consistency : 一致性 ,,, 事务开始之前和事务结束之后,数据库的完整性没有被破坏
  • isolation : 隔离性,, 多个事务执行,会隔离,,事务隔离等级:读未提交,读已提交,可重复读,串行化
  • durability : 持久性,,,事务结束之后,对数据的修改是永久的
spring中的事务
编程式事务:

使用TransactionManager,, 先传入数据源配置一个TransactionManager,

java 复制代码
   /**
     * 事务管理器
     * @return
     */
    @Bean
    PlatformTransactionManager platformTransactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }

transaction.getTransaction() 获取一个事务,,
transaction.commit() : 提交事务
transaction.rollback() : 回滚事务

java 复制代码
  public void transfer(String from,String to,Double money){

        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(definition);

        try {
            accountDao.minusMoney(from,money);
//            int i = 1/0;
            accountDao.addMoney(to,money);

            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            System.out.println(e.getMessage());

            // 回滚事务
            transactionManager.rollback(status);
        }
    }

}

也可以用 transactionTemplate ,, spring利用aop的思想,把很多重复的东西封装了,比如jdbcTeamplate,transactionTemplate

java 复制代码
    @Bean
    TransactionTemplate transactionTemplate(){
    // 传入一个 transactionManager
        return new TransactionTemplate(platformTransactionManager());
    }
java 复制代码
    public void transfer(String from,String to,Double money){

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
             
                    accountDao.minusMoney(from,money);
            int i = 1/0;
                    accountDao.addMoney(to,money);

                 
               
            }
        });

    }

编程式事务一般不用,因为还是有侵入的代码,事务和业务的代码混在一起

声明式事务

声明式事务本质上是aop,,需要配置tx:advice ,,事务的通知,,也就是需要被增强的方法名字,,,

aop将这个通知配置进去。。。

声明式事务底层就是aop,需要添加包:

xml 复制代码
     <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.4</version>
        </dependency>
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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.cj.ts02"/>

    <context:property-placeholder location="db.properties"/>
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
    </bean>

    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
     </bean>

<!--    声明式事务,本质还是aop-->
<!--    要增强的方法-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
<!--            配置事务的属性,哪些方法要加事务,可以使用通配符 -->
            <tx:method name="transfer"/>
<!--            <tx:method name="add*"/>-->
        </tx:attributes>
    </tx:advice>

<!--    aop配置,,,   相当于把这个transfer方法动态代理了,,-->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.cj.ts02.AccountService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>
</beans>

java代码实现事务:使用EnableTransactionManager 开启事务,,开启了之后,,就能直接使用@Transactional注解去标识,哪个方法要有事务

java 复制代码
@Configuration
@ComponentScan(basePackages = "com.cj.ts02")
@PropertySource("classpath:db.properties")
// 开启事务注解,, 开了之后只需要在方法上添加 @Transactional 即可
@EnableTransactionManagement
public class JavaConfig {

    @Value("${db.url}")
    private String url;
    @Value("${db.username}")
    private String username;
    @Value("${db.password}")
    private String password;


    @Bean
    public JdbcTemplate jdbcTemplate(){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
        return jdbcTemplate;
    }

    @Bean
    DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setUrl(url);
        return ds;
    }

    /**
     * 事务管理器
     * @return
     */
    @Bean
    PlatformTransactionManager platformTransactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }
}

java和xml结合使用,,, 在xml中可以使用<tx:annotation-driven/> : 这个配置表示启用事务注解,,相当于@EnableTransactionManager


自定义事务注解

自己定义一个注解,用来回滚事务,,,在切面中使用编程式注解,,,将事务融入到切面中

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyTx {
}
java 复制代码
@Component
@Aspect
@EnableAspectJAutoProxy
public class MyTxAspect {

    @Autowired
    PlatformTransactionManager transactionManager;


    @Pointcut("@annotation(MyTx) || @within(MyTx)")
    public void pc(){}


    @Around("pc()")
    public Object around(ProceedingJoinPoint pjp){
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(definition);
        Object result = null;
        try {
            result = pjp.proceed();
            transactionManager.commit(status);

        } catch (Throwable e) {
            System.out.println("e.getMessage() = " + e.getMessage());
            transactionManager.rollback(status);
//            throw new RuntimeException(e);
        }

        return result;

    }
}
spring事务的属性
  • 隔离性 : spring中的隔离性和 mysql中的隔离性是差不多的
    @Transactional(isolation = Isolation.DEFAULT)

    default: 表示mysql使用哪种隔离级别,spring就用哪种

  • 传播性 : 一个方法中有事务,,里面又调用了另一个方法的事务,,,两个事务怎么处理。。。。 多个事务怎么处理就是传播性

    • REQUIRED : 如果事务存在,不会开启一个新的事务,,加入到之前存在的事务里面,,,,最多只有一个事务
    • REQUIRES_NEW: 如果事务存在,会将当前事务挂起,开启一个新的事务
    • NESTED : 嵌套事务,, 相当于设置了一个保存点,,事务可以全部回滚,也可以回滚到保存点,,, 嵌套事务,就是在第一个方法结束的时候设置一个保存点,,, 里面的方法报错,会回滚到这个保存点,不会影响外面的方法,,, 如果外面的方法报错,,因为是一个事务,,里面的是不会提交的,所以里面的也会跟着回滚
    • MANDATORY : 加在子方法上,,表示外面的方法必须有事务,,没有事务会抛错
    • SUPPORTS: 如果存在事务就加入,,如果没有事务,就以非事务的方式去运行
    • NOT_SUPPORTED: 就要以非事务的方式进行,,如果存在事务,将当前事务挂起,,执行完成之后再执行事务
    • NEVER : 就是不在事务里面运行,如果存在事务,会报错
事务回滚机制

不是所有异常都会回滚,,默认是运行时异常才会回滚。。,, @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,rollbackFor = Exception.class) 设置rollbackFor ,指定什么异常回滚,, 设置noRollbackFor,指定什么异常不回滚

是否只读

只读事务一般设置在查询方法上,但不是所有的查询方法都需要只读事务。。。如果一个业务中有多个查询sql,,每个查询sql都会开启一个独立的事务,,如果有并发操作修改了数据,,那么每个sql查询出来的结果可能会不一样,,

开启只读事务就是将多个查询sql放入到一个事务中,多条相同的sql在该事务中执行将会获取到相同的查询结果

java 复制代码
  @Transactional(propagation = Propagation.REQUIRED,readOnly = true)
超时时间

有的时候发生死锁,不可能一直在那等着,,

java 复制代码
 @Transactional(propagation = Propagation.REQUIRED,timeout = 10)
事务失效

声明式事务失效,,aop什么时候失效,,,,aop需要生成子类,,不能用final修饰

  • 方法自调用失效
    事务调用的是代理对象中的方法,,,如果方法自调用,,调的是this自己的方法,不是代理对象的方法
    解决: 自己注入自己,,然后调用
  • 异常被捕获
  • 方法是非public, 被aop代理,aop需要生成子类,private私有方法,不能重写
  • 非运行时异常 : 默认捕获运行时异常,,
  • 不是spring bean,,, 对象必须是spring创建的
  • 数据库不支持事务

在spring中加事务的方式都一样,,,spring中将事务的实现都抽象出来了,有一套抽象的解决方案,,,框架只需要实现接口,就能使用spring中的事务

TransactionManager : 事务管理器

TransactionDefinition : 事务的定义

TransactionStatus : 事务的状态

PlatformTransactionManager

  • getTransaction()
相关推荐
Tech Synapse13 分钟前
java 如何暴露header给前端
java·开发语言·前端
海涛高软19 分钟前
python一堆数字相近的分成一组
开发语言·python
酷酷学!!!22 分钟前
C++第二弹 -- C++基础语法下(引用 内联函数 auto关键字 范围for 指针空值)
开发语言·c++
从后端到QT23 分钟前
Qt 线程 QThread类详解
开发语言·qt
长亭外的少年30 分钟前
Java 8 到 Java 22 新特性详解
java·开发语言
大霸王龙33 分钟前
Python的`queue`模块
开发语言·python
2301_8031101333 分钟前
����: �Ҳ������޷��������� javafx.fxml ԭ��: java.lang.ClassNotFoundException解决方法
java
only-lucky1 小时前
C语言:高级并发操作(信号)
c语言·开发语言
nbplus_0071 小时前
golang扩展 日志库ZAP[uber-go zap]切割 natefinch-lumberjack
开发语言·后端·golang·个人开发·日志切割·logger
GSDjisidi1 小时前
日本IT-SIER/SES的区别详情、契约形态等
java·大数据·c语言·c++·php