当前位置: 首页 > news >正文

网站直播间怎么做网站集约化建设的优点

网站直播间怎么做,网站集约化建设的优点,网站关键词在哪里看,net网站开发教学视频通过上一章的学习#xff0c;我们了解了 Spring Data 操作数据库的一些常见问题。这一章我们聊一聊数据库操作中的一个非常重要的话题——事务管理。 Spring 事务管理包含两种配置方式#xff0c;第一种是使用 XML 进行模糊匹配#xff0c;绑定事务管理#xff1b;第二种是…通过上一章的学习我们了解了 Spring Data 操作数据库的一些常见问题。这一章我们聊一聊数据库操作中的一个非常重要的话题——事务管理。 Spring 事务管理包含两种配置方式第一种是使用 XML 进行模糊匹配绑定事务管理第二种是使用注解这种方式可以对每个需要进行事务处理的方法进行单独配置你只需要添加上 Transactional然后在注解内添加属性配置即可。在我们的错误案例示范中我们统一使用更为方便的注解式方式。 另外补充一点Spring 在初始化时会通过扫描拦截对事务的方法进行增强。如果目标方法存在事务Spring 就会创建一个 Bean 对应的代理Proxy对象并进行相关的事务处理操作。 在正式开始讲解事务之前我们需要搭建一个简单的 Spring 数据库的环境。这里我选择了当下最为流行的 MySQL Mybatis 作为数据库操作的基本环境。为了正常使用我们还需要引入一些配置文件和类简单列举一下。 1.数据库配置文件 jdbc.properties配置了数据连接信息。 jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/spring?useUnicodetruecharacterEncodingUTF-8serverTimezoneUTCuseSSLfalse jdbc.usernameroot jdbc.passwordpass 2.JDBC 的配置类从上述 jdbc.properties 加载相关配置项并创建 JdbcTemplate、DataSource、TransactionManager 相关的 Bean 等。 public class JdbcConfig {Value(${jdbc.driver})private String driver;Value(${jdbc.url})private String url;Value(${jdbc.username})private String username;Value(${jdbc.password})private String password;Bean(name jdbcTemplate)public JdbcTemplate createJdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}Bean(name dataSource)public DataSource createDataSource() {DriverManagerDataSource ds new DriverManagerDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(username);ds.setPassword(password);return ds;}Bean(name transactionManager)public PlatformTransactionManager createTransactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);} } 3.应用配置类通过注解的方式配置了数据源、MyBatis Mapper 的扫描路径以及事务等。 Configuration ComponentScan Import({JdbcConfig.class}) PropertySource(classpath:jdbc.properties) MapperScan(com.spring.puzzle.others.transaction.example1) EnableTransactionManagement EnableAutoConfiguration(exclude{DataSourceAutoConfiguration.class}) EnableAspectJAutoProxy(proxyTargetClass true, exposeProxy true) public class AppConfig {public static void main(String[] args) throws Exception {ApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);} } 完成了上述基础配置和代码后我们开始进行案例的讲解。 案例 1unchecked 异常与事务回滚 在系统中我们需要增加一个学生管理的功能每一位新生入学后都会往数据库里存入学生的信息。我们引入了一个学生类 Student 和与之相关的 Mapper。 其中Student 定义如下 public class Student implements Serializable {private Integer id;private String realname;public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String getRealname() {return realname;}public void setRealname(String realname) {this.realname realname;} }Student 对应的 Mapper 类定义如下 Mapper public interface StudentMapper {Insert(INSERT INTO student(realname) VALUES (#{realname}))void saveStudent(Student student); }对应数据库表的 Schema 如下 CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT,realname varchar(255) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; 业务类 StudentService其中包括一个保存的方法 saveStudent。执行一下保存一切正常。 接下来我们想要测试一下这个事务会不会回滚于是就写了这样一段逻辑如果发现用户名是小明就直接抛出异常触发事务的回滚操作。 Service public class StudentService {Autowiredprivate StudentMapper studentMapper;Transactionalpublic void saveStudent(String realname) throws Exception {Student student new Student();student.setRealname(realname);studentMapper.saveStudent(student);if (student.getRealname().equals(小明)) {throw new Exception(该学生已存在);}} }然后使用下面的代码来测试一下保存一个叫小明的学生看会不会触发事务的回滚。 public class AppConfig {public static void main(String[] args) throws Exception {ApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);StudentService studentService (StudentService) context.getBean(studentService);studentService.saveStudent(小明);} } 执行结果打印出了这样的信息 Exception in thread main java.lang.Exception: 该学生已存在 at com.spring.puzzle.others.transaction.example1.StudentService.saveStudent(StudentService.java:23) 可以看到异常确实被抛出来但是检查数据库你会发现数据库里插入了一条新的记录。 但是我们的常规思维可能是在 Spring 里抛出异常就会导致事务回滚而回滚以后是不应该有数据存入数据库才对啊。而在这个案例中异常也抛了回滚却没有如期而至这是什么原因呢我们需要研究一下 Spring 的源码来找找答案。 案例解析 我们通过 debug 沿着 saveStudent 继续往下跟得到了一个这样的调用栈 从这个调用栈中我们看到了熟悉的 CglibAopProxy另外事务本质上也是一种特殊的切面在创建的过程中被 CglibAopProxy 代理。事务处理的拦截器是 TransactionInterceptor它支撑着整个事务功能的架构我们来分析下这个拦截器是如何实现事务特性的。 首先TransactionInterceptor 继承类 TransactionAspectSupport实现了接口 MethodInterceptor。当执行代理类的目标方法时会触发 invoke()。由于我们的关注重点是在异常处理上所以直奔主题跳到异常处理相关的部分。当它 catch 到异常时会调用 completeTransactionAfterThrowing 方法做进一步处理。 protected Object invokeWithinTransaction(Method method, Nullable Class? targetClass,final InvocationCallback invocation) throws Throwable {//省略非关键代码Object retVal;try {retVal invocation.proceedWithInvocation();}catch (Throwable ex) {completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}//省略非关键代码 } 在 completeTransactionAfterThrowing 的代码中有这样一个方法 rollbackOn()这是事务的回滚的关键判断条件。当这个条件满足时会触发 rollback 操作事务回滚。 protected void completeTransactionAfterThrowing(Nullable TransactionInfo txInfo, Throwable ex) {//省略非关键代码//判断是否需要回滚if (txInfo.transactionAttribute ! null txInfo.transactionAttribute.rollbackOn(ex)) {try {//执行回滚txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}catch (TransactionSystemException ex2) {ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {throw ex2;}}//省略非关键代码 } rollbackOn() 其实包括了两个层级具体可参考如下代码 public boolean rollbackOn(Throwable ex) {// 层级 1根据rollbackRules及当前捕获异常来判断是否需要回滚RollbackRuleAttribute winner null;int deepest Integer.MAX_VALUE;if (this.rollbackRules ! null) {for (RollbackRuleAttribute rule : this.rollbackRules) {// 当前捕获的异常可能是回滚“异常”的继承体系中的“一员”int depth rule.getDepth(ex);if (depth 0 depth deepest) {deepest depth;winner rule;}}}// 层级 2调用父类的 rollbackOn 方法来决策是否需要 rollbackif (winner null) {return super.rollbackOn(ex);}return !(winner instanceof NoRollbackRuleAttribute); } 1. RuleBasedTransactionAttribute 自身的 rollbackOn() 当我们在 Transactional 中配置了 rollbackFor这个方法就会用捕获到的异常和 rollbackFor 中配置的异常做比较。如果捕获到的异常是 rollbackFor 配置的异常或其子类就会直接 rollback。在我们的案例中由于在事务的注解中没有加任何规则所以这段逻辑处理其实找不到规则即 winner null进而走到下一步。 2. RuleBasedTransactionAttribute 父类 DefaultTransactionAttribute 的 rollbackOn() 如果没有在Transactional 中配置 rollback 属性或是捕获到的异常和所配置异常的类型不一致就会继续调用父类的 rollbackOn() 进行处理。 而在父类的 rollbackOn() 中我们发现了一个重要的线索只有在异常类型为 RuntimeException 或者 Error 的时候才会返回 true此时会触发 completeTransactionAfterThrowing 方法中的 rollback 操作事务被回滚。 public boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error); } 查到这里真相大白Spring 处理事务的时候如果没有在 Transactional 中配置 rollback 属性那么只有捕获到 RuntimeException 或者 Error 的时候才会触发回滚操作。而我们案例抛出的异常是 Exception又没有指定与之匹配的回滚规则所以我们不能触发回滚。 问题修正 从上述案例解析中我们了解到Spring 在处理事务过程中并不会对 Exception 进行回滚而会对 RuntimeException 或者 Error 进行回滚。 这么看来修改方法也可以很简单只需要把抛出的异常类型改成 RuntimeException 就可以了。于是这部分代码就可以修改如下 Service public class StudentService {Autowiredprivate StudentMapper studentMapper;Transactionalpublic void saveStudent(String realname) throws Exception {Student student new Student();student.setRealname(realname);studentMapper.saveStudent(student);if (student.getRealname().equals(小明)) {throw new RuntimeException(该用户已存在);}}再执行一下这时候异常会正常抛出数据库里不会有新数据产生表示这时候 Spring 已经对这个异常进行了处理并将事务回滚。 但是很明显这种修改方法看起来不够优美毕竟我们的异常有时候是固定死不能随意修改的。所以结合前面的案例分析我们还有一个更好的修改方式。 具体而言我们在解析 RuleBasedTransactionAttribute.rollbackOn的代码时提到过 rollbackFor 属性的处理规则。也就是我们在Transactional 的 rollbackFor 加入需要支持的异常类型在这里是 Exception就可以匹配上我们抛出的异常进而在异常抛出时进行回滚。 于是我们可以完善下案例中的注解修改后代码如下 Transactional(rollbackFor Exception.class) 再次测试运行你会发现一切符合预期了。 案例 2试图给 private 方法添加事务 接着上一个案例我们已经实现了保存学生信息的功能。接下来我们来优化一下逻辑让学生的创建和保存逻辑分离于是我就对代码做了一些重构把 Student 的实例创建和保存逻辑拆到两个方法中分别进行。然后把事务的注解 Transactional 加在了保存数据库的方法上。 Service public class StudentService {Autowiredprivate StudentMapper studentMapper;Autowiredprivate StudentService studentService;public void saveStudent(String realname) throws Exception {Student student new Student();student.setRealname(realname);studentService.doSaveStudent(student);}Transactionalprivate void doSaveStudent(Student student) throws Exception {studentMapper.saveStudent(student);if (student.getRealname().equals(小明)) {throw new RuntimeException(该用户已存在);}} } 执行的时候继续传入参数“小明”看看执行结果是什么样子 异常正常抛出事务却没有回滚。明明是在方法上加上了事务的注解啊为什么没有生效呢我们还是从 Spring 源码中找答案。 案例解析 通过 debug我们一步步寻找到了问题的根源得到了以下调用栈。我们通过 Spring 的源码来解析一下完整的过程。 前一段是 Spring 创建 Bean 的过程。当 Bean 初始化之后开始尝试代理操作这个过程是从 AbstractAutoProxyCreator 里的 postProcessAfterInitialization 方法开始处理的 public Object postProcessAfterInitialization(Nullable Object bean, String beanName) {if (bean ! null) {Object cacheKey getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) ! bean) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean; } 我们一路往下找暂且略过那些非关键要素的代码直到到了 AopUtils 的 canApply 方法。这个方法就是针对切面定义里的条件确定这个方法是否可以被应用创建成代理。其中有一段 methodMatcher.matches(method, targetClass) 是用来判断这个方法是否符合这样的条件 public static boolean canApply(Pointcut pc, Class? targetClass, boolean hasIntroductions) {//省略非关键代码for (Class? clazz : classes) {Method[] methods ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {if (introductionAwareMethodMatcher ! null ?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :methodMatcher.matches(method, targetClass)) {return true;}}}return false; } 从 matches() 调用到了 AbstractFallbackTransactionAttributeSource 的 getTransactionAttribute public boolean matches(Method method, Class? targetClass) {//省略非关键代码TransactionAttributeSource tas getTransactionAttributeSource();return (tas null || tas.getTransactionAttribute(method, targetClass) ! null); } 其中getTransactionAttribute 这个方法是用来获取注解中的事务属性根据属性确定事务采用什么样的策略。 public TransactionAttribute getTransactionAttribute(Method method, Nullable Class? targetClass) {//省略非关键代码TransactionAttribute txAttr computeTransactionAttribute(method, targetClass);//省略非关键代码} } 接着调用到 computeTransactionAttribute 这个方法其主要功能是根据方法和类的类型确定是否返回事务属性执行代码如下 protected TransactionAttribute computeTransactionAttribute(Method method, Nullable Class? targetClass) {//省略非关键代码if (allowPublicMethodsOnly() !Modifier.isPublic(method.getModifiers())) {return null;}//省略非关键代码 } 这里有这样一个判断 allowPublicMethodsOnly() !Modifier.isPublic(method.getModifiers()) 当这个判断结果为 true 的时候返回 null也就意味着这个方法不会被代理从而导致事务的注解不会生效。那此处的判断值到底是不是 true 呢我们可以分别看一下。 条件 1allowPublicMethodsOnly() allowPublicMethodsOnly 返回了 AnnotationTransactionAttributeSource 的 publicMethodsOnly 属性。 protected boolean allowPublicMethodsOnly() {return this.publicMethodsOnly; } 而这个 publicMethodsOnly 属性是通过 AnnotationTransactionAttributeSource 的构造方法初始化的默认为 true。 public AnnotationTransactionAttributeSource() {this(true); } 条件 2Modifier.isPublic() 这个方法根据传入的 method.getModifiers() 获取方法的修饰符。该修饰符是 java.lang.reflect.Modifier 的静态属性对应的几类修饰符分别是PUBLIC: 1PRIVATE: 2PROTECTED: 4。这里面做了一个位运算只有当传入的方法修饰符是 public 类型的时候才返回 true。 public static boolean isPublic(int mod) {return (mod PUBLIC) ! 0; } 综合上述两个条件你会发现只有当注解为事务的方法被声明为 public 的时候才会被 Spring 处理。 问题修正 了解了问题的根源以后解决它就变得很简单了我们只需要把它的修饰符从 private 改成 public 就可以了。 不过需要额外补充的是我们调用这个加了事务注解的方法必须是调用被 Spring AOP 代理过的方法也就是不能通过类的内部调用或者通过 this 的方式调用。所以我们的案例的 StudentService它含有一个自动装配Autowired了自身StudentService的实例来完成代理方法的调用。这个问题我们在之前 Spring AOP 的代码解析中重点强调过此处就不再详述了。 Service public class StudentService {Autowiredprivate StudentMapper studentMapper;Autowiredprivate StudentService studentService;public void saveStudent(String realname) throws Exception {Student student new Student();student.setRealname(realname);studentService.doSaveStudent(student);}Transactionalpublic void doSaveStudent(Student student) throws Exception {studentMapper.saveStudent(student);if (student.getRealname().equals(小明)) {throw new RuntimeException(该学生已存在);}} } 重新运行一下异常正常抛出数据库也没有新数据产生事务生效了问题解决。 Exception in thread main java.lang.RuntimeException:该学生已存在  at com.spring.puzzle.others.transaction.example2.StudentService.doSaveStudent(StudentService.java:27)   重点回顾 通过以上两个案例相信你对 Spring 的声明式事务机制已经有了进一步的了解最后总结下重点: Spring 支持声明式事务机制它通过在方法上加上 Transactional表明该方法需要事务支持。于是在加载的时候根据 Transactional 中的属性决定对该事务采取什么样的策略Transactional 对 private 方法不生效所以我们应该把需要支持事务的方法声明为 public 类型Spring 处理事务的时候默认只对 RuntimeException 和 Error 回滚不会对 Exception 回滚如果有特殊需要需要额外声明例如指明 Transactional 的属性 rollbackFor 为 Exception.class
http://www.zqtcl.cn/news/75870/

相关文章:

  • 摄影网站的意义电商平台网站
  • 做一个静态网站需要多少钱著名的外贸网站
  • 南京网站搭建大唐网站建设
  • 做网站法律条文郑州校园兼职网站建设
  • 网站开发遇到的问题及解决方法广州社交软件app开发
  • 网站建设哪里专业织梦网站上传及安装步骤
  • 互动网站建设公司平台外宣推广技巧
  • 怎么做网站登录界面网站傻瓜式建设
  • 网站建设标书模版大型手机网站制作
  • 免费创建个人博客网站小程序商店网址
  • 网站制作代码应用商店网站模板
  • 贴吧aso优化贴吧阜新网站优化
  • 网站建设主体是什么大丰做网站的公司
  • 无锡网站策划公司最简单的软件开发工具
  • 网站制作过程简介旅游网站网页设计图片
  • 当前网站开发用什么软件百度指数排名热搜榜
  • 各大网站vip接口建设手工艺品网站模板
  • 亚马逊网站特点和经营范围医院网站建设企业
  • 广州电力建设有限公司网站衡水做wap网站多少钱
  • 怎么做网站里导出没有水印的图做白酒用哪个分类信息网站
  • 做的网站怎么上传wordpress登入不了
  • 如何由网页生成网站企业开发软件公司拓展方案
  • 购物网站类型50篇经典软文100字
  • 什么类型的网站流量高系统优化的方法哲学
  • 站牛网是做什么的wordpress文章自动中文
  • 做网站用php吗wordpress安装插件返回空白
  • 网站运营与管理的一个目的苏州工业园区图片
  • 郑州大型网站开发公司更改wordpress用户名
  • 长春网络建站模板wordpress修改自己的头像
  • 怎么能加强门户网站建设daocloud wordpress