DAO中发生了哪些事情(连接池、mybatis、事务管理)?

  2019-8-10 


连接池、mybatis、事务管理等

数据库连接池

数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放。
为什么要使用带连接池的数据源呢,最根本的原因还是因为每次创建连接开销比较大,频繁的创建和关闭数据库连接将会严重的影响性能。因此,常用的做法是维护一个数据库连接池,每次使用完之后并不是直接关闭数据库连接,再后面如果需要创建数据库连接的时候直接拿之前释放的数据库连接使用,避免频繁创建和关闭数据库连接造成的开销。

连接池配置在数据源DataSource属性中

在实验室项目中使用dbcp(commons-dbcp包)作为数据库连接池,只需要将dbcp配置写在Spring配置中的数据源bean中(DataSource)即可,class位dhcp,见前面spring-mybatis的例子(然后mybatis配置mysql数据源)

当然如果DataSource内容是单独写在db.properties里的话也可以直接写进去

那么Mybatis是如何使用连接池的呢?见此文:https://blog.csdn.net/majinggogogo/article/details/71715846

当我们需要创建SqlSession对象并需要执行SQL语句时(表现在用户调用mybatis生成的代理对象的DAO方法时),这时候MyBatis才会去调用dataSource对象来创建java.sql.Connection对象,这就建立了数据库连接了。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。也就是在这个时候,数据库连接池起作用了。【】


(补充)几种数据源配置:

https://blog.csdn.net/u013490585/article/details/61201840

Spring事务管理

事务:事务就是并发控制的单位,是用户定义的一个操作序列。它满足ACID属性

事务管理,实质上就是按照给定的事务规则(事务传播行为,事务超时时间等)来执行提交或者回滚操作

Spring事务管理由PlatformTransactionManager 事务管理器(比如提交,回滚,获取状态等方法都在这里定义),TransactionStatus事务状态(设置回滚,是否回滚,是否完成,是否是新事务等),TransactionDefinition基本事务属性的定义(事务属性包括以下) 三个核心接口构成

Spring的事务主要是ThreadLocal和AOP去做实现的

事务属性包括事务隔离级别、事务传播行为、事务超时、事务的只读属性、事务的回滚规则

事务属性:事务隔离级别

事务隔离级别(4种):若干个并发的事务之间的隔离程度

TransactionDefinition.ISOLATION_DEFAULT:使用底层数据库的默认隔离级别

数据库有以下四种隔离级别:

【读未提交】:TransactionDefinition.ISOLATION_READ_UNCOMMITTED:可读取另一个事务未提交的数据(啥都防不了

注意RU读未提交:指的是线程A修改后还没有提交!万一线程A没修改完,还在继续修改,或者线程A回滚了,那这个时候线程B读取的就是脏数据,此即脏读。这个时候脏是指的读取了线程A没有完成修改的数据,只修改了一半

读未提交指的是线程A修改后还没有提交!万一线程A没修改完,还在继续修改,或者线程A回滚了,那这个时候线程B读取的就是脏数据,此即脏读

【读已提交】:TransactionDefinition.ISOLATION_READ_COMMITTED:只能读另一个事务已提交的数据,保证了读者在一次读中看不到另一个事务未提交的数据(防脏读

【可重复读】:TransactionDefinition.ISOLATION_REPEATABLE_READ:可以保证只要事务A开始后之后,反复怎么读都能看到一样的东西(防脏读,不可重复读)无法防幻读,因为不能防止insert数据【这是mysql的默认隔离级别】

【串行化】:TransactionDefinition.ISOLATION_SERIALIZABLE:事务依次执行(防脏读,不可重复读,幻读)。但性能差

mysql默认使用可重复读,是因为历史原因,实际上我们最好使用读已提交【不提】

事务属性:事务传播行为

事务传播行为(7种):当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

①、PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。

②、PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。

③、PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。

④、PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。

⑤、PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。

⑥、PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。

⑦、PROPAGATION_NESTED :nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。

事务属性:Other

事务超时(超时自动回滚)

事务的只读属性

事务的回滚规则(抛出异常则默认回滚,若没有抛出异常则提交)

Spring事务管理如何配置使用

Spring事务管理:编程式,声明式(最好)

(使用tx.spring包)

且使用了事务管理器之后,我们也不必手动配置AOP,Spring自动为我们进行事务AOP设置

以下使用注解方式进行声明式事务管理

将@Transactional注释在接口,接口方法,类,类方法上,这样就不需要在xml配置

举例,为方法加上事务属性:

@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) 
{
  return bankDao.transfer(fromId, toId, amount);
}

注解方式只需要配置事务管理器和AOP:

<!-- 1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 2 声明使用注解配置事务管理器,并声明AOP配置方案
 \* transaction-manager 事务管理器
 \* proxy-target-class
 true : 底层强制使用cglib 代理
 -->
 <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/> 

使用了这个tx:annotation-driven声明后,指定了aop实现方案,那么我们就不必再用< aop:advisor>来配置事务管理AOP来

(不这样写的话得【】:https://www.jianshu.com/p/40f79da0cdef)

参考:https://zhuanlan.zhihu.com/p/37108469

具体使用在哪?

数据库-DAO-DAO业务层接口-业务层

我们一般将事务设置在【DAO业务接口层的方法】上,举个例子:

@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) 
{
    //bankDao即为DAO对象,在mybatis配置中是SqlSession根据我写的<DAO方法,SQL语句>为我们生成的代理对象
  	bankDao.transfer(fromId, toId, amount);
    bankDao.deletcount(Id);
}

这个方法需要满足事务的ACID特性,那我们就在这个方法上用@Transactional配置方法的事务属性(注解式)

总结:DAO中发生了哪些事情?

【Spring事务管理器处理DAO-业务接口层事务】

【mybatis处理DAO访问数据库具体操作】

【Mybatis使用Datasource连接池处理DAO的JDBC请求】


【mybatis部分】我们需要写DAO接口,接口中存在DAO方法来访问数据库,每个DAO方法中对应一个SQL语句(mapper映射文件)。mybatis框架通过datasource等配置生成sqlsession工厂,再由这个工厂生产sqlseesion,通过sqlsession获取代理mapper对象。

就相当于这些DAO方法虽然我们没有具体实现,但sqlsession根据我们写的<方法-SQL>帮我们处理了细节,封装在一个代理对象中,就是mapper对象,在这个对象中就封装有具体的JDBC访问。

【连接池部分】在配置datasource的时候就指定了连接池,就可以在mybatis为我们JDBC创建连接(底层隐式自动)的时候,自动管理并发的数据库连接请求了

【Spring提供的事务管理tx-spring】然而多线程每个连接数据库的线程都是一个需要满足ACID的事务,这时候就需要进行事务管理(隔离级别,传播级别等,是否已建立连接等等),而tx-spring已经为我们提供了事务管理工具类,我们在Spring中配置好事务管理器,然后为不同的方法(我们想为DAO方法提供事务管理,那么就要为DAO方法配置)进行事务管理配置(可以在代码中用注解@Transactional直接在方法上配置,由于我们使用基于注解的mybatis,那么实际上我们就将@Transactional配置在DAO接口(也是基于注解的mapper)的方法上!)

【将事务管理以AOP织入DAO方法】使用tx:annotation-driven在里面设置好proxy就不需要我们配置AOP了。


且听风吟