mybatis01
一、事务隔离机制
事务的隔离级别描述的是多个事务之间的可见性问题。比如一个事务还未提交时,其他事务是否能看到被未提交事务修改的数据。
1.1幻读、不可重复读取、脏读
幻读(虚读)
select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入。或不存在,执行delete删除,却发现删除成功。
在一个事务的多次查询之间, 允许另一个事务进行插入、删除操作
不可重复读取
事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录; 在一个事务处理过程中读取了另一个事务中修改并已提交的数据, 导致两次查询结果不一致
在一个事务的多次查询之间, 允许另一个事务进行更新操作
脏读
事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。 通俗的说,脏读就是指在一个事务处理过程中读取了另一个未提交的事务中的数据 , 导致两次查询结果不一致
一个事务在更新未提交时, 允许另一个事务读取
1.2spring隔离级别可选值
读未提交(read uncommitted )
读未提交(read uncommitted ):事务中的修改,即使没有提交,对其它事务也是可见的。当前事务可以读取其他事务未提交的数据,也称之为脏读。这个场景很容易理解,不再赘述。
读已提交(read committed )
读已提交(read committed ):只能读取到其他事务已经提交的数据,可以避免脏读。读已提交不能保证可重复读,也就是说,前后两次读取,会获取到不同的结果集。
可重复读 (repeatable read)
可重复读 (repeatable read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。会出现幻读的情况,所谓的幻读,就是前后两次读取到的记录行数不一致。事务A在读取某些记录时,事务B又在事务A内插入了一条新记录,当事务A再次读取该范围的记录时,发现前后两次读取的结果集不是完全一致。
串行读(Serializable)
串行读(Serializable):是最高的隔离级别,通过强制的事务串行执行,避免了前面说的幻读问题。简单的来说,Serializable会在读取的每张表上加锁,所以可能导致大量的超时和锁竞争的问题。实际应用中很少用到这一级别。
Mysql默认隔离级别:REPEATABLE-READ
Oracle默认隔离级别:Read committed
隔离级别 | 说明 |
---|---|
ISOLATION_DEFAULT(默认级别) | 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别. |
ISOLATION_READ_UNCOMMITTED(读未提交) | 这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。 这种隔离级别会产生脏读,不可重复读和幻像读。 |
ISOLATION_READ_COMMITTED(读已提交) | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读(因为在事务提交前允许别的事务更新和插入操作)。 |
ISOLATION_REPEATABLE_READ(可重复读) | 它保证了一个事务不能修改已经由另一个事务读取但还未提交的数据,也就是说同一个事务内读取的数据都是一致的。这种事务隔离级别可以防止脏读、不可重复读。但是可能出现幻像读(一个事务在提交前不允许别的事务更新,但是允许插入)。 |
ISOLATION_SERIALIZABLE(串行化) | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 除了防止脏读,不可重复读外,还避免了幻像读(一个事务在提交前不允许别的事务读取、更新、插入)。 |
1.3spring如何设置隔离级别
二、mybatis
2.1什么是mybatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2.2ORM思想
映射: 对象关系映射 ORM Object-Relational-Mapping 解决了java类和mysql数据库表的映射问题。
2.2.1Hibernate
SSH框架 :Spring、Struts2、HIbernate
全自动的ORM框架(不需要写sql)
HIbernateUtil.query(User.class); 查询表中所有数据
HIbernateUtil.query(user); 查询当前对象相关的数据
开发效率高,运行效率低。
2.2.2mybatis(ibatis)
SSM框架:Spring、Springmvc、Mybatis
半自动的ORM框架(需要写sql语句,但是不需要手动的给参数赋值以及手动的封装结果集)
2.2.3持久层技术
JDBC HIbernate Mybatis
底层 框架 框架
运行效率最高 最低 中间
开发效率最低 最高 中间
2.3mybatis操作流程
2.3.1引入jar包
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.17</version>
</dependency>
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
2.3.2mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd">
<!--配置-->
<configuration><!--引入外部属性配置文件--><properties resource="db.properties"></properties><!--数据库连接环境配置 当前使用的默认环境--><environments default="development"><!--单独的环境 id="环境名 唯一标识"--><environment id="development"><!--事务管理 type="jdbc" 使用jdbc的事务管理机制--><transactionManager type="JDBC"/><!--数据源配置 type="POOLED|UNPOOLED" 使用连接池|不使用连接池--><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment><!--多个环境--><environment id="test"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><!--映射器 指定sqlMapper文件位置--><mappers><mapper resource="mapper/AccountMapper.xml"/></mappers>
</configuration>
2.3.3java文件和sql文件相分离
@Data
public class Account {private int id;private String name;private Double money;
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis01.mapper"><!--id=“sql语句唯一标识” resultType="返回结果集类型"--><select id="findAccount" resultType="com.hl.mybatis01.pojo.Account">select * from account where id = #{id}</select>
</mapper>
2.3.4mybatis框架核心类,内部执行过程
@Testvoid query() throws IOException {//1.加载mybatis核心配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");//2.创建SqlSessionFactorySqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);//3.基于工厂获取会话连接SqlSession session = factory.openSession();//4.获取SqlMapper文件AccountMapper accountMapper = session.getMapper(AccountMapper.class);//5.执行sql语句
// Account account = session.selectOne("com.hl.mybatis01.mapper.findAccount",1);
// System.out.println(account);//推荐模式Account account = accountMapper.findAccount(1);System.out.println(account);List<Account> list = accountMapper.findAllAccount();System.out.println(list);Account account1 = new Account();account1.setId(1);account1.setMoney(10000.0);int num = accountMapper.updateAccount(account1);System.out.println("更新的数据条数:"+num);//增删改操作 提交事务session.commit();//6.关闭流程session.close();}
2.4mybatis面向接口开发
package com.hl.mybatis01.mapper;import com.hl.mybatis01.pojo.Account;public interface AccountMapper {Account findAccount(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间 唯一标识-->
<mapper namespace="com.hl.mybatis01.mapper.AccountMapper"><!--id=“sql语句唯一标识” resultType="返回结果集类型"--><select id="findAccount"resultType="com.hl.mybatis01.pojo.Account">select * from account where id = #{id}</select>
<!-- //查询所有账户-->
<!-- List<Account> findAllAccount();--><select id="findAllAccount" resultType="com.hl.mybatis01.pojo.Account">select * from account</select>
<!-- //更新一个账户 根据id更新余额-->
<!-- int updateAccount(Account account);--><update id="updateAccount" >update account set money=#{money} where id=#{id}</update>
<!-- //删除一个账户-->
<!-- int deleteAccount(int id);--><delete id="deleteAccount">delete from account where id = #{id}</delete>
<!-- //新增一个账户-->
<!-- int insertAccount(Account account);--><insert id="insertAccount">insert into account(name,money) values (#{name},#{money})</insert>
</mapper>
public interface AccountMapper {//根据id查询单个账户Account findAccount(int id);//查询所有账户List<Account> findAllAccount();//更新一个账户 根据id更新余额int updateAccount(Account account);//删除一个账户int deleteAccount(int id);//新增一个账户int insertAccount(Account account);
}
面试题
1、事务的四个特性
特性 | 说明 | 示例 |
---|---|---|
原子性 (Atomicity) | 事务是不可分割的工作单位,要么全部执行,要么全部不执行 | 银行转账:A转B100元,必须同时完成A账户减100和B账户加100 |
一致性 (Consistency) | 事务执行前后,数据库从一个一致状态变到另一个一致状态 | 转账前后,A和B账户总额保持不变 |
隔离性 (Isolation) | 多个事务并发执行时,一个事务的执行不应影响其他事务 | 事务A读取数据时,事务B不能同时修改该数据 |
持久性 (Durability) | 事务一旦提交,其结果就是永久性的,即使系统故障也不丢失 | 转账成功后,即使系统崩溃,转账记录仍然存在 |
2、事务的传播机制
传播行为 | 说明 | 适用场景 |
---|---|---|
REQUIRED (默认) | 当前有事务则加入,没有则新建 | 大多数业务方法 |
REQUIRES_NEW | 总是新建事务,暂停当前事务(如果存在) | 日志记录、独立业务 |
SUPPORTS | 当前有事务则加入,没有则以非事务运行 | 查询方法 |
NOT_SUPPORTED | 以非事务方式执行,暂停当前事务(如果存在) | 不需要事务的操作 |
MANDATORY | 必须在事务中运行,否则抛出异常 | 强制要求事务的方法 |
NEVER | 必须在非事务环境下执行,否则抛出异常 | 与MANDATORY相反 |
NESTED | 当前有事务则在嵌套事务内执行,没有则新建 | 复杂业务中的子操作 |
3、事务的隔离机制
事务隔离机制:
isolation = Isolation.READ_UNCOMMITTED
READ_UNCOMMITTED 读未提交 读取到其他事务修改但是尚未提交的数据 可能出现脏数据
READ_COMMITTED 读已提交 读取其他事务已经提交的数据
REPEATABLE_READ 可重复读 一个事务在执行期间,不受其他会话影响,多次读取结果保持一致
SERIALIZABLE 可串行化 锁表 第一个事务commit提交后,第二个事务才能执行
隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 | 数据库默认 |
---|---|---|---|---|---|
READ_UNCOMMITTED | ❌ 可能 | ❌ 可能 | ❌ 可能 | 最高 | - |
READ_COMMITTED | ✅ 防止 | ❌ 可能 | ❌ 可能 | 高 | Oracle, SQL Server |
REPEATABLE_READ | ✅ 防止 | ✅ 防止 | ❌ 可能 | 中 | MySQL |
SERIALIZABLE | ✅ 防止 | ✅ 防止 | ✅ 防止 | 最低 |
-
脏读:读取到其他事务未提交的数据
-
不可重复读:同一事务内多次读取同一数据结果不同
-
幻读:同一事务内多次查询返回的结果集不同(新增/删除行)
4、事务注解
@EnableTransactionManagement 开启全局事务支持
@Transactional 使用事务
readOnly = true, 只读事务 当前事务中查询出的数据,不能用户更新操作
rollbackFor = Exception.class 当遇到特定异常,执行rollback回滚
noRollbackFor = {} 当发生某种异常,不回滚
timeout=数值 超时时间
propagation = 事务的传播机制 a--调用-->b b是否能够使用事务的问题
Propagation.REQUIRED
isolation = 事务的隔离行为 两个方法在使用事务时,对方操作的数据是否可见