连接池
在实际开发中都会使用连接池,因为他可以减少我们获取连接所消耗的时间。连接池就是用于存储连接的一个容器,这个容器其实就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接,该集合还必须实现队列的特点:先进先出。
Mybatis中的连接池
Mybatis连接池的分类
可以看出 Mybatis 将它自己的数据源分为三类:
- UNPOOLED :不使用连接池的数据源
- POOLED: 使用连接池的数据源
- JNDI :使用 JNDI 实现的数据源
具体结构如下:
相应地, MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource, PooledDataSource 类来表示 UNPOOLED、 POOLED 类型的数据源
在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据 库连接,也就是我们所说的连接池技术) 。
Mybatis 中数据源的配置
我们的数据源配置就是在 SqlMapConfig.xml 文件中的<dataSource type=>, 具体配置如下:
-
POOLED:MyBatis 会创建 PooledDataSource 实例,会采用传统的javax.sql.DataSource规范中的连接池,Mybatis有针对规范的实现,他是从池中获取一个连接来用
<dataSource type="POOLED"> <property name="driver" value=""></property> <property name="url" value=""></property> <property name="username" value=""></property> <property name="password" value=""></property> </dataSource>
-
UNPOOLED:MyBatis 会创建 UnpooledDataSource 实例,虽然也实现了javax.sql.DataSource接口,但是 并没有使用池的思想,每次创建一个新的连接来用
<dataSource type="UNPOOLED"> <property name="driver" value=""></property> <property name="url" value=""></property> <property name="username" value=""></property> <property name="password" value=""></property> </dataSource>
-
JNDI:是SUN公司推出的一套规范,属于JavaEE技术之一。目的是模仿windows系统中的注册表。采用服务器提供的JNDI技术实现,来获取DataSource对 象,不同的服务器所能拿到DataSource是不一样。 mybaties会从在应用服务器向配置好的JNDI数据源DataSource获取数据库连接。一般在生产环境中使用
SqlMapConfig.xml中的配置: <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql环境 --> <environment id="mysql"> <!-- 配置事务 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置连接池 UNPOOLED POOLED JNDI --> <dataSource type="JNDI"> <property name="data_source" value="java:comp/env/jdbc/text"/> </dataSource> </environment> </environments> 以Tomcat为例,在Tomcat/conf/context.xml配置如下: <?xml version="1.0" encoding="UTF-8"?> <Context> <Resource name="jdbc/test" 数据源的名称 type="javax.sql.DataSource" 数据源类型 auth="Container" 数据源提供者 maxActive="20" 最大活动数 maxWait="10000" 最大等待时间 maxIdle="5" 最大空闲数 username="root" 用户名 password="123" 密码 driverClassName="com.mysql.jdbc.Driver" 驱动类 url="jdbc:mysql://localhost:3306/chatroom" 连接url字符串 />
注意:
-
当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象 来创建java.sql.Connection对象。也就是说, java.sql.Connection对象的创建一直延迟到执行SQL语句 的时候。
-
MyBatis 是 通 过 工 厂 模 式 来 创 建 数 据 源 DataSource 对 象 的 , MyBatis 定 义 了 抽 象 的 工 厂 接 口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法返回数据源 DataSource。
-
MyBatis 创建了 DataSource 实例后,会将其放到 Configuration 对象内的 Environment 对象中, 供 以后使用。
-
图示:
-
事务
什么是事务
所谓事务(Transaction ),是指一个操作序列,这些操作序列要么都被执行,要么都不被执行,它是一个不可分割的工作单元。
事务的四个特性(ACID)
- 原子性(Atomicity):操作这些指令时要么全部成功,要么全部失败,只要其中一个指令失败则全部指令执行失败,数据回滚。
- 一致性(Consistency):事务的执行使数据从一个状态转换成另一个状态,但是对于整个数据的完整性保持一致。例如,A和B两者共有1000元,无论转账几次,事务结束后总额还是1000元
- 隔离性(Isolation):当多个用户并发访问数据库时,数据库为每一个用户开启的事务不能被其他事务干扰,多个并发事务间要相互隔离
- 持久性(Durability):当事务正确完成后,他对于数据的改变是永久性的。
不考虑隔离性,并发事务会导致的三个问题
- 脏读:一个事务读取了另一个事务未提交的数据。例如:A对B转账,事务1执行了B+100,但是并未提交,此时事务2 读取了事务1 未提交的数据,显示B多了100。如果此时事务1进行了回滚,这时就造成了脏读。
- 不可重复读:在一个事务中读取两次某个数据,读出来的数据不一致。事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读。
- 幻读:一个事务先后读取一个范围的记录,两次读取的记录不同。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读
注意:
- 脏读和不可重复读的区别:脏读是某一事务读取了另一事务未提交的数据;而不可重复读则是读取了另一事务提交的数据
- 幻读和不可重复读的区别:两者都是读取了另一个事务提交的数据,不同的是不可重复读读取的是同一个数据项,幻读针对的是一批数据整体,比如数据个数
解决方法(四种隔离级别)
-
Read Uncommited(最低级别,任何情况都无法保证)
读未提交。就是一个事务可以读取另一个未提交事务的数据
-
Read Commited(可避免脏读)
读提交。一个事务要等另一个事务提交后才能读取数据
-
Repeatable Read(可避免脏读,不可重复读)
重复读。在开始读取数据(事务开启)时,不再允许修改操作。重复读可以解决不可重复读问题。应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作
-
Serializable(可避免脏读、不可重复读、幻读)
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
注意:
- 大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。
- 隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。
- 设置数据库的隔离级别一定要是在开启事务之前。
Mybatis中的事务
他是通过SqlSession对象的commit()方法和rollback()方法实现事务的提交和回滚。底层都是JDBC的操作。因为在执行时Mybatis会设置setAutoCommit()为false,所以每次我们必须通过SqlSession.commit()手动提交。我们也可以设置自动提交,在获取SqlSession对象时:session = factory.openSession(true);这样就可以设置自动提交,但就编程而言,设置自动提交为false,再根据情况决定是否提交,这种方式更常用。