深入浅出springboot阅读学习(二)

1、数据库,springboot中配置mysql只需要mysql-connector-java以及spring-boot-starter-jdbc两个依赖即可,当然如果配置连接池我们可以使用commons-dbcp2依赖。我们操作数据库主要的方式是JdbcTemplate,Hibernate和MyBatis当然实际中一般是后两种。jdbcTemplate基本步骤就是创建jdbcTemplate实例,然后拼装sql语句,拼装参数,jdbcTemplate提交。对于我们默认一般的jdbcTemplate是每次调用都会产生一次连接.query(sql1,rowMapper),.update(sql2)这是两个连接,但是我们也可以通过StatementCallback以及ConnectionCallback在一次连接请求中执行多条sql,详见page89 5-12代码段,书本《深入浅出springboot》;第二种常用方式是JPA,JPA是通过Hibernate方式来操作数据库的,JPA核心是维护了实体类,通过@Entity/@Table/@Id/@Column等注解,Springboot提供了两个注解来扫描JPA@EnableJpaRepositories和@EntityScan第一个标识启用JPA第二个标识对实体Bean的扫描;第三种是MyBatis,maven中引入mybatis-spring-boot-starter依赖,mybatis核心是mappers映射器,我们在mapper文件中namespace对应执行到接口、注意在配置文件上配置mybatis的映射文件和扫描别名、启动类上@MapperScan("com.winter.dao")配置扫描路径。
2、数据库事务,如果我们需要自己在代码里手动实现一个事务语句,可以这样实现,dataSource.getConnection()获取连接,conn.setAutoCommit(false)开启事务,conn.setTransactionIsolation(隔离级别)设置隔离级别,conn.prepareStatement(sql)以及ps.executeUpdate()这两句执行sql也是真正涉及业务的,conn.commit()提交事务包括以上一段都要用try包起来用catch(Exception e) 捕获异常异常里面conn.rollback()语句进行回滚,最后在finally语句中conn.close()关闭连接。从上面可以看出我们自己实现写起来复杂只有两句提交涉及业务其余的都是为了事务以及基础判断处理。所以我们引入了AOP对业务之外的代码用AOP进行织入管理。在我们的业务层,我们引入@Transactional当其在类上时标识所有公共非静态的方法都将启用事务功能。这个@Transactional可以根据其属性设置数据库事务、事务回退的异常类等。当然这个注解也可以放在方法上,但是不推荐放在接口上AOP生效就是基于接口的代理了,但是接口代理你就只能JDK动态代理了,而不能CGLIB动态代理。注意几个使用注意事项:1.接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效。2.接口中异常(运行时异常)被捕获而没有被抛出。默认配置下,spring 只有在抛出的异常为运行时 unchecked 异常时才回滚该事务,也就是抛出的异常为RuntimeException 的子类(Errors也会导致事务回滚),而抛出 checked 异常则不会导致事务回滚 。可通过 @Transactional rollbackFor进行配置。3.多线程下事务管理因为线程不属于 spring 托管,故线程不能够默认使用 spring 的事务,也不能获取spring 注入的 bean 。在被 spring 声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。一个使用了@Transactional 的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务。

https://blog.csdn.net/deniro_li/article/details/83927739 

二次梳理:start
数据库隔离性分为四个级别:
第一类更新丢失:一个事务提交一个事务回滚,此问题各类数据库均已解决。
第二类更新丢失:多个事务同时提交,出现更新丢失问题
事务默认是独立的互不感知,很简单的例子两个事务更新同一条数据如果A B B A执行方式导致B被覆盖掉。
1、未提交读:允许一个事务读取另外一个事务未提交的数据。出现的问题就是脏读,比如100张票,A事务购买1张,减去还剩99,此时事务B很快的过来操作读取到99减掉1张然后提交98,但是A事务后续执行发生异常回滚导致回滚成其旧数据100,更新丢失。
2、读写提交:一个事务只能读取其他事务已经提交完成的数据,说白了就是如果被操作的数据还没有提交,此时读取的数据就是老的。注意此种隔离方式表示的是只有别人
读写提交后的数据才能读取,但是依旧允许并发,此时就相当于A事务读取100,扣减1张然后99,这时候B事务来了不会等待读取的是100,扣减后提交事务99,然后A事务回滚,由于第一类更新一个提交一个回滚的问题已经解决,所以此时不会出现问题结果正确。但是会出现如下问题,不可重复读场景,比如超售现象,比如库存为1,事务A读取扣减还未提交,此时事务B来了直接读取扣减,然后事务A提交为0了,然后事务B再扣减就会导致超售,不可重复读现象。
3、为了解决以上问题,提出了可重复读隔离级别,这个隔离级别就是事务A操作的读写的时候事务B需要等到事务A操作完提交事务后才能进行读操作。这样基本就大大限制了并发操作。所以截止可重复读隔离级别起码是改变了两个事务同时操作数据库数据导致数据之间干扰影响实际应用的情况。但是可重复读隔离级别也存在一些问题,幻读,幻读出现的场景是:100件商品,每次交易一件,当前A事务读取库存50件,B事务查看交易,50笔,A进行交易并提交事务此时就是49件商品51笔交易,然后B事务再打印交易信息发现变成51。可重复读能解决不可重复读问题,这个是针对同一条数据就不多说了,这个幻读,其实是针对多条信息,但是你操作的这一条信息会影响其他信息的结果。可重复读通过行锁就可以解决,但是幻读那就应该是串行化了。
4、所谓串行化就是所有的SQL都会按照顺序进行操作,这样无论肯定是不会出现任何问题,数据必定一致性。
end
总结下:未提交读,存在脏读,不可重复读,幻读;读写提交,存在不可重复读,幻读;可重复读,存在幻读;串行化,没有任何问题。选择隔离级别要根据具体场景比如秒杀如果选择串行化那多线程也就废了,一般主要还是读写提交级别为主,为了克服数据不一致和性能问题,还开发了乐观锁,甚至不再使用数据库而是用其他手段比如redis作为数据载体。同时不同类型数据库的支持隔离级别以及默认级别也不一样,Oracle只能支持读写提交和串行化,默认读写提交,mysql四种都支持默认可重复读级别。至于所谓的隔离级别在@Transactional上的应用也很简单@Transactional(isolation=Isolation.SERIALIZABLE)即可进行设置,并且如果一个个执行麻烦的话可以通过配置文件进行批量指定。
传播行为:传播行为是方法之间调用事务采取的策略问题。根据枚举一共有7种,比较常用的有三种REQUIRED,这也是默认的,需要事务,有事务则沿用,没有则创建新的;REQUIRED_NEW无论当前事务是否存在,都会创建一个新的事务,并且新的事务会根据新的配置去指定隔离级别、锁等特性;NESTED 这种传播行为可以实现调用子方法时子方法出现异常只回滚子方法而不回滚当前方法的事务,此种回滚是利用了数据库的保存点,当有的数据库不支持保存点则利用REQUIRED_NEW创建新的事务去运行你的代码。下面说下注意事项@Transactional的失效场景,最主要的一个就是类内部的自调用,说白了事务时基于AOP,前面了解过AOP是动态代理,如果类的自调用是不会走代理对象去调用的所以要注意要使其符合AOP应用场景。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值