MyBatis相关

MyBatis核心组件:

  • SqlSessionFactoryBuilder(构造器): 它可以从XML、注解或者手动配置Java代码来创建SqlSessionFactory。
  • SqlSessionFactory: 用于创建SqlSession (会话) 的工厂
  • SqlSession: SqlSession是Mybatis最核心的类,可以用于执行语句、提交或回滚事务以及获取映射器Mapper的接口
  • SQL Mapper: 它是由一个Java接口和XML文件(或注解)构成的,需要给出对应的SQL和映射规则,它负责发送SQL去执行,并返回结果

 

通过配置文件创建sqlSessionFactory工厂:

sqlSessionFactory = new SqlSessionFactoryBuilder()
                    .build(Resources.getResourceAsStream("mybatis-config.xml"));

我们使用sqlSession.getMapper()方法获取UserMapper对象,实际上这里我们是获取了UserMapper接口的代理类,然后再由代理类执行方法。

try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//在这里才创建数据库连接对象Connection User user
= userMapper.selectById(1); System.out.println("User : " + user); }

 在myBatis中如何使用连接池:

    在mybatis中,定义了一个数据库连接池状态的类PoolState,在这个类里,除维护了数据源实例,还维护着数据库连接。数据库连接被分成了两种状态类型并存放在两个列表中:idleConnections和activeConnections。    

idleConnections:

  空闲(idle)状态PooledConnection对象被放置到此集合中,表示当前闲置的没有被使用的PooledConnection集合,调用PooledDataSource的getConnection()方法时,会优先从此集合中取PooledConnection对象。当用完一个java.sql.Connection对象时,MyBatis会将其包裹成PooledConnection对象放到此集合中。

activeConnections:

  活动(active)状态的PooledConnection对象被放置到名为activeConnections的ArrayList中,表示当前正在被使用的PooledConnection集合,调用PooledDataSource的getConnection()方法时,会优先从idleConnections集合中取PooledConnection对象,如果没有,则看此集合是否已满,如果未满,PooledDataSource会创建出一个PooledConnection,添加到此集合中,并返回。

 https://www.cnblogs.com/yougewe/articles/10061276.html

 获取连接池中的connection

@Override
public Connection getConnection() throws SQLException {
    // 调用 popConnection()
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}

参考:Mybatis源码解读

https://juejin.im/post/5cd4355be51d453a5143306c

 

关于#{}和${}

#{}可以防止sql注入(比如where ‘1’=‘1’这种情况)

#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号

$方式一般用于传入数据库对象,例如传入表名.

MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

参考:

https://www.cnblogs.com/baizhanshi/p/5778692.html

https://www.cnblogs.com/200911/p/5869097.html

Mybatis所用到的设计模式

http://www.crazyant.net/2022.html

Mybatis命名空间namespace作用:

1.定义mapper接口,面向接口编程。

2.在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使在不同映射文件中的语句ID相同,也不会再产生冲突了。

Mybatis中一级二级缓存:

先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。
 
一级缓存:
 
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。
 
一级缓存的工作原理:
 
用户发起查询请求,查找某条数据,sqlSession先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但sqlSession执行commit(一般是增删改才有commit),即增删改操作时会清空缓存。这么做的目的是避免脏读。
如果commit不清空缓存,会有以下场景:A查询了某商品库存为10件,并将10件库存的数据存入缓存中,之后被客户买走了10件,数据被delete了,但是下次查询这件商品时,并不从数据库中查询,而是从缓存中查询,就会出现错误。
既然有了一级缓存,那么为什么要提供二级缓存呢?
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句(同一个Mapper就是一个二级缓存),多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。
还有一个原因,实际开发中,MyBatis通常和Spring进行整合开发。Spring将事务放到Service中管理,对于每一个service中的sqlsession是不同的,这是通过mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer创建sqlsession自动注入到service中的。 每次查询之后都要进行关闭sqlSession,关闭之后数据被清空(mybatis默认开启一级缓存)。所以spring整合之后,如果没有事务,一级缓存是没有意义的。
 
二级缓存
二级缓存原理:(同一个mapper有一个二级缓存)
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
 
开启二级缓存:
mybatis配置文件里面:
<!--开启二级缓存-->    
<setting name="cacheEnabled" value="true"/>   

对于两个不同的SqlSession查同一个mapper文件里面的内容,是会把sqlsession里面的一级缓存存到二级缓存里面的。

Spring和MyBatis整合时, 每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务(有事务,在同一个事务里面,同一个SqlSession还不会关闭),一级缓存是没有意义的。那么如果开启二级缓存,关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。这样,缓存在sqlsession关闭之后依然存在。
 
一级二级缓存总结:
对于查询多commit少且用户对查询结果实时性要求不高,此时采用mybatis二级缓存技术降低数据库访问量,提高访问速度。
 
但不能滥用二级缓存,二级缓存也有很多弊端,从MyBatis默认二级缓存是关闭的就可以看出来。
二级缓存是建立在同一个namespace下的,如果对表的操作查询可能有多个namespace,那么得到的数据就是错误的(简单说就是对同一个表的操作尽量放到一个mapper里面)。
举个简单的例子:
订单和订单详情,orderMapper、orderDetailMapper。在查询订单详情时我们需要把订单信息也查询出来,那么这个订单详情的信息被二级缓存在orderDetailMapper的namespace中,这个时候有人要修改订单的基本信息,那就是在orderMapper的namespace下修改,他是不会影响到orderDetailMapper的缓存的,那么你再次查找订单详情时,拿到的是缓存的数据,这个数据其实已经是过时的。
 
根据以上,想要使用二级缓存时需要想好两个问题:
1)对该表的操作与查询都在同一个namespace下,其他的namespace如果有操作,就会发生数据的脏读。
2)对关联表的查询,关联的所有表的操作都必须在同一个namespace。
posted @ 2019-04-04 15:03  LeeJuly  阅读(119)  评论(0)    收藏  举报