1.mybatis中的连接池
1.1什么是连接池
连接池是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个池子,叫连接池。由应用程序动态地对池中的连接 进行申请、使用和释放。应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。连接池是面向数据库连的,是为了优 化数据库连接资源。
1.2Mybatis中的连接池
在 Mybatis 的配置文件中,通过<dataSource type="pooled”>来实 现 Mybatis 中连接池的配置。在mybatis中数据库连接池可以分 为以下三类:1.UNPOOLED不使用连接池的数据源。2.POOLED使用连接池的数据源。3.JNDI使用JNDI实现数据库连接池
1.3 UNPOOLED连接过程分析
UNPOOLED 不使用连接池的数据源,当 dateSource 的type属性被配置成了UNPOOLED,MyBatis 首先会实例化一个 UnpooledDataSourceFactory工厂实例,然后通过.getDataSource() 方法返回一个UnpooledDataSource 实例对象引用
public class UnpooledDataSource implements DataSource {
private ClassLoader driverClassLoader;
private Properties driverProperties;
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap();
private String driver;
private String url;
private String username;
private String password;
private Boolean autoCommit;
private Integer defaultTransactionIsolationLevel;
public UnpooledDataSource() {
}
public UnpooledDataSource(String driver, String url, String username, String password){
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
public Connection getConnection() throws SQLException {
return this.doGetConnection(this.username, this.password);
}
private Connection doGetConnection(String username, String password) throws SQLException {
Properties props = new Properties();
if(this.driverProperties != null) {
props.putAll(this.driverProperties);
}
if(username != null) {
props.setProperty("user", username);
}
if(password != null) {
props.setProperty("password", password);
}
return this.doGetConnection(props);
}
private Connection doGetConnection(Properties properties) throws SQLException {
this.initializeDriver();
Connection connection = DriverManager.getConnection(this.url, properties);
this.configureConnection(connection);
return connection;
}
}
1.4 POOLED 数据源 连接池
PooledDataSource: 将java.sql.Connection对象包裹成PooledConnection对象放到了PoolState类型的容器中维护。 MyBatis将连接池 中的PooledConnection分为两种状态: 空闲状态(idle)和活动状态(active),这两种状态的PooledConnection对象分别被存储到 PoolState容器内的idleConnections和activeConnections两个List集合中
空闲(idle)状态PooledConnection对象被放置到此集合中,表示当前闲置的没有被使用的PooledConnection集合,调用 PooledDataSource的getConnection()方法时,会优先从此集合中PooledConnection对象。
活动(active)状态的PooledConnection对象被放置到名为activeConnections的ArrayList中,表示当前正在被使用的PooledConnection 集合,调用PooledDataSource的getConnection()方法时,会优先从idleConnections集合中取PooledConnection对象
2.mybatis中的事务和隔离级别
2.1jdbc中的事务
Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC的 setAutoCommit()方法来设置事务提交 方式的。
2.2mybatis中的事务提交方式
Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制。@BeforeEach: 在每个方法之前加一下些操作 @AfterEach: 在每个方法之后加一些操作。我们设置了自动提交事务之后,就不需要在进行commit操作了
2.3事务的回滚
为了保证数据的一致性我们就需要进行事务的回滚
//用于在测试方法执行之前执行
@BeforeEach
public void init()throws Exception{
// 设置为自动提交
sqlSession = MybatisUtils.openSession();
//获取dao的代理对象
userMapper = sqlSession.getMapper(IUserMapper.class);
}
// 在测试结束之后执行
@AfterEach
public void destroy()throws Exception{
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
@Test
public void testRollback(){
try{
User condition = new User();
condition.setId(1);
condition.setNickname("尚云科技111299");
int i = userMapper.update(condition);
assertEquals(i,1);
throw new Exception();
}catch (Exception e){
e.printStackTrace();
// 进行数据回滚操作
sqlSession.rollback();
}
}
2.4事务的隔离级别
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问 这个数据,然后使用了这个数据。
不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据
脏读 | 不可重复读 | 幻读 | 说明 | |
Read uncommitted | √ | √ | √ | 直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别 的事务可以读到这个改变.这是很不安全的。允许任务读取数据库中未提交的数据更改,也称为脏读。 |
Read committed | × | √ | √ | 直译就是"读提交",可防止脏读,意思就是语句提交以后即执行了COMMIT以后,别的事务才能读到这个改变. 只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 |
Repeatable read | × | × | √ | 直译就是"可以重复读",这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读。 |
Serializable | × | × | × | 直译就是"序列化",意思是说这个事务执行的时候不允许别的事务并发执行. 完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞 |
3.延迟加载策略
3.1什么是延迟加载
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。优点:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
3.2association实现延迟加载
1.添加懒加载配置 2.aggressiveLazyLoading开启属性 3.配置每个关联字段的加载方式 4.association实现懒加载
4.使用注解进行开发
4.1 mybatis常用注解说明
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
4.2实现基本的CRUD
public interface IAddressDao {
@Insert("insert into t_address (addr, phone, postcode, user_id) VALUES (#{addr},#{phone},#{postcode},#{userId})")
int insert(Address address);
@Delete("delete from t_address where id = #{id}")
int delete(int id);
@Update("update t_address set addr = #{addr} where id = #{id}")
int update(Address address);
@Select("select * from t_address where id = #{id}")
Address selectById(int id);
}
4.3使用Result进行映射
@Select("select * from t_address where id = #{id}")
@Results(id = "addressRes", value = {
//id = true 标志这个字段是主键
@Result(id = true, column = "id", property = "id"),
@Result(column = "addr", property = "addr"),
@Result(column = "phone", property = "mobile"),
})
Address selectById(int id);
4.4注解进行关联查询
4.4.1一对一
@Select("select * from t_address where id = #{id}")
@Results(id = "addressRes", value = {
//id = true 标志这个字段是主键
@Result(id = true, column = "id", property = "id"),
@Result(column = "addr", property = "addr"),
@Result(column = "phone", property = "mobile"),
@Result(column = "user_id", property = "user",
one = @One(select = "com.tledu.erp.mapper.IUserMapper.selectById", fetchType = FetchType.EAGER))
})
Address selectById(int id);
4.4.2一对多
@Select("select * from t_user where id = #{id}")
@Results(id = "addressRes", value = {
//id = true 标志这个字段是主键
@Result(id = true, column = "id", property = "id"),
@Result(column = "id", property = "addressList",
many = @Many(select = "com.tledu.erp.mapper.IAddressMapper.listByUserId", fetchType = FetchType.EAGER))
})
User selectById(int id);
5.缓存
Mybatis 中缓存分为一级缓存,二级缓存
5.1一级缓存
一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
5.2二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。
注意开启二级缓存需要配置<setting name="cacheEnabled" value="true"/>
@Data
public class Address implements Serializable {
private Integer id;
private String addr;
private String phone;
private String postcode;
private Integer userId;
/**
* 创建用户,关联用户表
*/
private User user;
}