JDBC-JDBC到ORM的事务实现-《Java笔记》

admin 2025-10-19 03:54:15 编程 来源:ZONE.CI 全球网 0 阅读模式

Java JDBC ORM

一、JDBC

早期SUN公司想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动。数据库连接池:C3P0、DBCP—%20Apache%20CommonPool、Druid、Hikari

二、ORM

Hibernate

Hibernate%20是一个开源的对象关系映射框架,它对JDBC%20进行了非常轻量级的对象封装,它将%20POJO%20与数据库表建立映射关系,是一个全自动的%20orm%20框架,hibernate%20可以自动生成%20SQL%20语句,自动执行,使得%20Java%20程序员可以使用面向对象的思维来操纵数据库。Hibernate%20需要定义实体类和%20hbm%20映射关系文件(IDE%20一般有工具生成)。Hibernate%20可以使用%20HQL、Criteria、Native%20SQL三种方式操作数据库。也可以作为%20JPA%20适配实现,使用%20JPA%20接口操作。

Mybatis

MyBatis%20是一款优秀的持久层框架,它支持定制化%20SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC%20代码和手动设置参数以及获取结果集。Mybatis%20可以使用简单的XML或注解来配置和映射原生信息,将接口和%20Java的POJOs(Plain%20Old%20Java%20Objects,普通的Java对象)映射成数据库中的记录

Mybatis与Hibernate的区别?

Hibernate是全自动,Mybatis是半自动。Mybatis

  • 优点:原生SQL(XML语法),直观,容易优化
  • 缺点:繁琐,可以用Mybatis-generator、Mybatis-Plus之类的插件弥补

Hibernate

  • 优点:简单场景不用写SQL(HQL、Cretiria、SQL)
  • 缺点:不好优化sql,对DBA不友好 Spring管理事务先看看JDBC如何操作事务?public%20void%20updatePrice()%20throws%20SQLException%20{
  • %20%20try%20{%20%20%20%20%20%20Connection%20conn%20=%20getConnection();%20%20%20%20%20%20//关闭自动提交%20%20%20%20%20%20conn.setAutoCommit(false);%20%20%20%20%20%20String%20sql%20=%20"update%20goods%20set%20price%20=?%20where%20id=?%20";%20%20%20%20%20%20PreparedStatement%20ptmt%20=%20conn.prepareStatement(sql);%20%20%20%20%20%20%20ptmt.setDouble(1,%203500);%20%20%20%20%20%20ptmt.setString(2,%20"1");%20%20%20%20%20%20//执行%20%20%20%20%20%20ptmt.execute();%20%20%20%20%20%20//提交事务%20%20%20%20%20%20conn.commit();%20%20}%20catch%20(Exception%20e)%20{%20%20%20%20%20%20//回滚事务%20%20%20%20%20%20conn.rollback();%20%20%20%20%20%20e.printStackTrace();%20%20}} 再看看Spring是如何无侵入的进行事务管理的?实现原理:事务管理器+AOP

    源码分析Spring事务实现过程

    示例代码:在goodsService.updatePrice方法上加了事务注解。
    1. @RequestMapping("/updateprice")
    2. public String updateprice(Double price,Integer age){
    3. goodsService.updatePrice(1,price);
    4. int i=10/0;
    5. userService.updateAge(1,age);
    6. return "sucess";
    7. }
  1. 请求进入Controller,调用goodsService的时候,调用的实际上是goodsService的代理对象

JDBC 到 ORM 的事务实现 - 图3

  1. 到代理类的方法中org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
  2. 进入事务拦截器的方法

JDBC 到 ORM 的事务实现 - 图4里面有个TransactionInterceptor

  1. TransactionInterceptor方法里面进行了事务管理

    1. protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
    2. final InvocationCallback invocation) throws Throwable {
    3. // If the transaction attribute is null, the method is non-transactional.
    4. TransactionAttributeSource tas = getTransactionAttributeSource();
    5. final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    6. final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    7. final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    8. if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
    9. // Standard transaction demarcation with getTransaction and commit/rollback calls.
    10. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
    11. Object retVal = null;
    12. try {
    13. // This is an around advice: Invoke the next interceptor in the chain.
    14. // This will normally result in a target object being invoked.
    15. //执行目标方法
    16. retVal = invocation.proceedWithInvocation();
    17. }
    18. catch (Throwable ex) {
    19. //回滚
    20. exception
    21. completeTransactionAfterThrowing(txInfo, ex);
    22. throw ex;
    23. }
    24. finally {
    25. cleanupTransactionInfo(txInfo);
    26. }
    27. //提交事务
    28. commitTransactionAfterReturning(txInfo);
    29. return retVal;
    30. }
    31. ...

    上述是简单场景的事务处理,如果是多个service方法,并且都加了@Transactional注解,那事务怎么算呢?那就需要学习Spring里的事务传播了。 ```java public Class ServiceA{

    @Transactional void methodA(){

    1. //....

    } }

public Class ServiceB{

  1. @Transactional
  2. void methodB(){
  3. //....
  4. }

} ```

七种事务的传播行为:

  • PROPAGATION_REQUIRED (默认) 表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。
  • PROPAGATION_SUPPORTS 表示当前方法不必须在一个具有事务的上下文中运行,如:ServiceA.methodA()调用ServiceB.methodB(),如果methodA方法上有事务,那么methodB加入他的事务,如果methodA上没有事务,那么methodB也不开事务
  • PROPAGATION_MANDATORY 必须被开启事务的方法调用,否则报错
  • PROPAGATION_REQUIRES_NEW 强制自己开启一个新的事务,如果一个事务已经存在,那么将这个事务挂起.如ServiceA.methodA()调用ServiceB.methodB()methodB()上的传播级别是PROPAGATION_REQUIRES_NEW的话,那么如果methodA报错,不影响methodB的事务,如果methodB报错,那么methodA是可以选择是回滚或者提交的,就看是否将methodB报的错误抛出还是try catch了.
  • PROPAGATION_NOT_SUPPORTED 总是非事务的执行,并且挂起任何事务.就是如果methodA方法执行到methodB这里了,methodA的事务就被挂起,然后methodB非事务的执行,然后等methodB方法运行结束,methodA的事务再继续.这个的好处就是methodB报错了不会让methodA回滚.
  • PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
  • PROPAGATION_NESTED 表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同propagation. required的一样

    事务失效的几个原因:

  1. Spring的事务注解@Transactional只能放在public修饰的方法上才起作用,如果放在其他非publicprivateprotected)方法上,虽然不报错,但是事务不起作用
  2. 如果采用Spring+SpringMVC,则context:component-scan重复扫描问题可能会引起事务失败。如果spring和mvc的配置文件中都扫描了service层,那么事务就会失效。原因:因为按照Spring配置文件的加载顺序来讲,先加载SpringMVC配置文件,再加载Spring配置文件,事物一般都在srping配置文件中进行配置,如果此时在加载srpingMVC配置文件的时候,把service也给注册了,但是此时事物还没加载,也就导致后面的事物无法成功注入到service中。所以把对service的扫描放在Spring配置文件中或是其他配置文件中。
  3. 如使用mysql且引擎是MyISAM,则事务会不起作用,原因是MyISAM不支持事务,可以改成InnoDB引擎
  4. @Transactional注解开启配置,必须放到listener里加载,如果放到DispatcherServlet的配置里,事务也是不起作用的。
  5. Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional注解,只能当设置了基于接口的代理时它才生效。因为注解是不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
  6. 在业务代码中如果抛出RuntimeException异常,事务回滚;但是抛出Exception,事务不回滚;默认对RuntimeException回滚
  7. 如果在加有事务的方法内,使用了try…catch..语句块对异常进行了捕获,而catch语句块没有throw new RuntimeExecption异常,事务也不会回滚
  8. 在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b,方法b里面的事务不会生效。原因是在同一个类之中,方法互相调用,切面无效,而不仅仅是事务。这里事务之所以无效,是因为Spring的事务是通过aop实现的。

代码示例:JDBC 到 ORM 的事务实现 - 图5可以看出这个this,并不是代理对象,事务也就不能生效了。

以太坊cppgolang区别 编程

以太坊cppgolang区别

以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
progolang 编程

progolang

Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
golangn个发送者 编程

golangn个发送者

Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
golang技能图谱 编程

golang技能图谱

从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
评论:0   参与:  0