• java基础巩固16


    JdbcTemplate用法

    (1)Spring提供的JdbcTemplate采用Template模式,提供了一些列以回调为特点的工具方法,目的是避免繁琐的try…catch语句。

    使用声明式服务

    (1)使用Spring操作JDBC的时候虽然方便,但是要在Spring中操作事务,就没必要手写JDBC事务,可以使用Spring提供的高级接口来操作事务。Spring提供了一个PlatformTransactionManager来表示事务管理器,所有的事务都由它负责管理。而事务由TransactionStatus表示。
    (2)Spring为什么要抽象出PlatformTransactionManager和TransactionStatus?原因是JavaEE除了提供JDBC事务外,它还支持分布式事务JTA(Java Transaction API)。分布式事务是指多个数据源(比如多个数据库,多个消息系统)要在分布式环境实现事务的时候,应该怎么实现。分布式事务实现起来非常复杂,简单地来说就是通过一个分布式事务管理器实现两阶段提交,但本身数据库事务就不快,基于数据库事务实现的分布式事务就慢的难以忍受,所以使用率不高。Spring为了同时支持JDBC和JTA两种事务模型,就抽象出platformTransactionManager。因为我们代码只需要JDBC事务,因此,在AppConfig中,需要再定义一个PlatformTransactionManager对应Bean,它的实际类型是DataSourceTransactionManager;
    (3)使用编程的方式使用Spring事务仍然比较繁琐,更好地方式是通过声明式事务来实现。使用声明式事务非常简单,除了在APPconfig中追加一个上述定义的PlatformTransactionManager外,再加一个@EnableTransactionManagement就可以启用声明式事务,然后对需要事务支持的方法,加一个@Transactional注解,或者直接在Bean的calss处加上,表示所有public方法都具有事务支持。

    回滚事务

    (1)默认情况下,如果发生了RuntimeException,Spring的声明式事务将自动回滚,在一个事务方法中,如果程序判断需要回滚事务,只需要抛出RuntimeException。如果要针对Checked Exception(必须被显式的抛出或者捕获的异常),需要在@Transactional注解中写出来,@Transactional(rollbackFor = {RuntimeException.class, IOException.class}),上述代码表示在抛出RuntimeException或者IOException时,事务将回滚。
    (2)在使用事务时,明确事务边界很重要。对于声明式事务,它的事务边界就是方法开始和结束。
    (3)Spring的声明式事务为事务传播定义了几个级别,默认传播级别就是REQUIRED,它的意思是,如果当前没有事务,就创建一个新事务,如果当前有事务,就加入到当前事务中执行。
    (4)Spring使用声明式事务,最终也是通过执行JDBC事务来实现功能,那么一个事务方法是如何获取到当前是否存在事务?答案是使用ThreadLocal。Spring总是把JDBC相关Connection和TransactionStatus实例绑定到ThreadLocal。如果一个事务方法从ThreadLocal未取到事务,那么它会打开一个新的JDBC连接,同时开启一个新的事务,否则,它就会直接使用从ThreadLocal获取的JDBC连接以及TransactionStatus。因此,事务能正确传播的前提是,方法调用实在一个线程内才行。换句话说,事务只能在当前线程传播,无法跨线程传播。如果想要实现跨线程传播事务,就要想办法把当前线程绑定到ThreadLocal的Connection和TransactionStatus实例传递给新线程。

    使用DAO

    (1)在传统的多层应用程序中,通常是Web层调用业务层,业务层调用数据访问层。业务层负责处理各种业务逻辑,而数据访问层只负责对数据进行增删改查。因此,实现数据访问层就是用JdbcTemplate实现对数据库的操作。编写数据访问层的时候,可以使用DAO模式。DAO就是Data Access Object的缩写。Spring提供了一个JdbcDaoSupport类,用于简化DAO的实现。
    (2)DAO模式就是一个简单的数据访问模式,是否使用DAO,根据实际情况决定,因为很多时候,直接在Service层操作数据库也是完全没有问题的。

    集成Hibernate

    (1)把关系数据库的表映射为Java对象的过程叫作ORM:Object Relational Mapping。ORM既可以把记录转换为Java对象,也可以把Java对象转化为行记录。使用JdbcTemplate配合RowMapper可以看作是最原始的ORM。如果要实现更自动化的ORM,可以选择成熟的ORM框架,例如Hibernate。
    (2)如果一个JavaBean被用于映射,我们就标记一个@Entity。

    集成MyBatis

    (1)使用Hibernate或者JPA操作数据库时,这类ORM干的主要工作就是把ResultSet的每一行变成Java Bean,或者把Java Bean自动住哪环岛Insert 或者Update语句的参数中,从而实现ORM。而ORM框架之所以知道如何把行数据映射到JavaBean,是因为我们在JavaBean的属性上给了足够的注解作为元数据,ORM框架获取到JAVA Bean的注解后,就知道如何双向映射。那么ORM框架是如何跟踪Java Bean的修改,以便于在update()操作中更新必要的属性?答案是使用Proxy模式,从ORM框架读取的USER实例实际上并不是User类,而是代理类,代理类继承自User类,但是针对每个setter方法做了覆写:
    public class UserProxy extends User {
    boolean _isNameChanged;

    public void setName(String name) {
        super.setName(name);
        _isNameChanged = true;
    }
    
    • 1
    • 2
    • 3
    • 4

    }
    这样,代理类可以跟踪到每个属性的变化。
    (2)针对一对多或多对一关系时,代理类可以直接通过getter方法查询数据库:
    public class UserProxy extends User {
    Session _session;
    boolean _isNameChanged;

    public void setName(String name) {
        super.setName(name);
        _isNameChanged = true;
    }
    
    /**
     * 获取User对象关联的Address对象:
     */
    public Address getAddress() {
        Query q = _session.createQuery("from Address where userId = :userId");
        q.setParameter("userId", this.getId());
        List
    list = query.list(); return list.isEmpty() ? null : list(0); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    }
    为了实现这样的查询,UserProxy必须保存Hibernate的当前session。但是,当事务提交后,Session自动关闭,此时再获取getAddress()将无法访问数据库,或者获取的不是事务一致的数据库。因此,ORM框架总是引入了Attached/Detached
    状态,表示当前此JavaBean到底实在Session的范围内,还是脱离了Session变成了一个游离对象。
    (3)此外,Hibernate和JPA为了实现兼容多种数据库,它使用HQL和JPQL查询,经过一道转换,编程特定数据库的SQL,理论上这样可以做到无缝切换数据库,但这一层自动转换除了少许的性能开销外,给SQL级别的优化带来了很多麻烦。
    (4)最后,ORM框架通常提供了缓存,并且还分为一级缓存和二级缓存,一级缓存是指在一个Session范围内的缓存,常见的情景是根据猪圈查询时,两次查询可以返回同一实例。二级缓存是指跨Session的缓存,一般默认为关闭,需要手动配置。二级缓存极大地增加了数据不一致性,原因在于SQL非常灵活,尝尝会导致以外的更新。例如:
    // 线程1读取:
    User user1 = session1.load(User.class, 123);

    // 一段时间后,线程2读取:
    User user2 = session2.load(User.class, 123);
    当二级缓存生效的时候,两个线程读取的User实例是一样的,但是数据库对应的行记录完全可能被修改。
    UPDATE users SET bonus = bonus + 100 WHERE createdAt <= ?
    ORM无法判断id=123的用户是否收到该UPDATE语句影响。考虑到数据库通常会支持多个应用程序,此UPDATE语句可能由其他进程执行,ORM框架就更不知道了,我们把这种ORM跨甲称之为全自动ORM框架。

    (5)对比Spring提供的JdbcTemplate,它和ORM框架相比,主要有几点差别:
    查询后需要手动提供Mapper实例以便于把ResultSet的每一行变为Java对象

    增删改查操作所需的参数列表,需要手动传入,即把User实例变为[user.id, user.name, user.email]这样的列表,比较麻烦。

    但是JdbcTemplate的优势在于它的确定性:即每次读取操作一定是数据库操作而不是缓存,所执行的SQL是完全确定的,缺点就是代码比较繁琐,构造INSERT INTO users VALUES (?,?,?)更是复杂。

    所以,介于全自动ORM如Hibernate和手写全部如JdbcTemplate之间,还有一种半自动的ORM,它只负责把ResultSet自动映射到Java Bean,或者自动填充Java Bean参数,但仍需自己写出SQL。MyBatis就是这样一种半自动化ORM框架。

  • 相关阅读:
    f2-python计算阶乘并写出过程--没搞出来--数字的阶乘
    二叉树进阶
    微服务项目:尚融宝(38)(核心业务流程:申请借款额度(1))
    Java 数组_方法_static关键字
    xctf攻防世界 Web高手进阶区 mfw
    Code Llama:Llama 2 学会写代码了!
    【pen200-lab】10.11.1.231
    GitHub的Java面试项目
    什么是网络爬虫?它是如何工作的以及如何自动抓取
    Deepin系统navicat15安装
  • 原文地址:https://blog.csdn.net/weixin_49131718/article/details/126728809